summaryrefslogtreecommitdiffstats
path: root/pbap
diff options
context:
space:
mode:
authorMatt Porter <mporter@konsulko.com>2018-06-25 11:01:11 -0400
committerMatt Porter <mporter@konsulko.com>2018-06-25 11:01:11 -0400
commit389b5994553fdae59d484068d542910efee79e9c (patch)
treed987b1aec2fed4da44e0b4776be4cea8dfde22be /pbap
parente8b328e0516d8ead9af13c8379a6cd1dbbb8a9e9 (diff)
Reorg binding-specific modules into subdirectories
Move binding-specific code into per-binding subdirectories. This separates the core message engine code and simplifies adding new binding-specific modules. Bug-AGL: SPEC-1525 Change-Id: I8fc545e3af375e2ed9e79a41363b7ade97e9b20a Signed-off-by: Matt Porter <mporter@konsulko.com>
Diffstat (limited to 'pbap')
-rw-r--r--pbap/CMakeLists.txt2
-rw-r--r--pbap/pbap.cpp294
-rw-r--r--pbap/pbap.h159
-rw-r--r--pbap/pbapmessage.cpp29
-rw-r--r--pbap/pbapmessage.h42
5 files changed, 526 insertions, 0 deletions
diff --git a/pbap/CMakeLists.txt b/pbap/CMakeLists.txt
new file mode 100644
index 0000000..99edd7d
--- /dev/null
+++ b/pbap/CMakeLists.txt
@@ -0,0 +1,2 @@
+add_headers(pbap.h pbapmessage.h)
+add_sources(pbap.cpp pbapmessage.cpp)
diff --git a/pbap/pbap.cpp b/pbap/pbap.cpp
new file mode 100644
index 0000000..43db8bd
--- /dev/null
+++ b/pbap/pbap.cpp
@@ -0,0 +1,294 @@
+/*
+ * Copyright (C) 2018 Konsulko Group
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <QMetaEnum>
+#include <QtQml/QQmlEngine>
+
+#include <vcard/vcard.h>
+
+#include "message.h"
+#include "messageengine.h"
+#include "pbap.h"
+#include "pbapmessage.h"
+#include "responsemessage.h"
+
+PhoneNumber::PhoneNumber(QString number, QString type)
+{
+ m_number = number;
+ m_type = stringToEnum(type);
+}
+
+PhoneNumber::~PhoneNumber()
+{
+}
+
+int PhoneNumber::stringToEnum(QString key)
+{
+ const QMetaObject* metaObject = PhoneNumber::metaObject();
+ int enumIndex = metaObject->indexOfEnumerator("PhoneNumberType");
+ QMetaEnum mEnum = metaObject->enumerator(enumIndex);
+
+ int value = mEnum.keyToValue(key.toUtf8().data());
+ return (value < 0) ? 0 : value;
+}
+
+Contact::Contact(QString name, QList<QObject *>numbers)
+{
+ m_name = name;
+ m_numbers = numbers;
+}
+
+Contact::~Contact()
+{
+}
+
+RecentCall::RecentCall(QString name, QString number, QString datetime, QString type)
+{
+ m_name = name;
+ m_number = number;
+ m_datetime = datetime;
+ m_type = stringToEnum(type);
+}
+
+RecentCall::~RecentCall()
+{
+}
+
+int RecentCall::stringToEnum(QString key)
+{
+ const QMetaObject* metaObject = RecentCall::metaObject();
+ int enumIndex = metaObject->indexOfEnumerator("RecentCallType");
+ QMetaEnum mEnum = metaObject->enumerator(enumIndex);
+
+ int value = mEnum.keyToValue(key.toUtf8().data());
+ return (value < 0) ? 0 : value;
+}
+
+Pbap::Pbap (QUrl &url, QQmlContext *context, QObject * parent) :
+ QObject(parent),
+ m_mloop(nullptr)
+{
+ m_mloop = new MessageEngine(url);
+ m_context = context;
+ m_context->setContextProperty("RecentCallModel", QVariant::fromValue(m_calls));
+ qmlRegisterUncreatableType<RecentCall>("RecentCall", 1, 0, "RecentCall", "Enum");
+
+ QObject::connect(m_mloop, &MessageEngine::connected, this, &Pbap::onConnected);
+ QObject::connect(m_mloop, &MessageEngine::disconnected, this, &Pbap::onDisconnected);
+ QObject::connect(m_mloop, &MessageEngine::messageReceived, this, &Pbap::onMessageReceived);
+}
+
+Pbap::~Pbap()
+{
+ delete m_mloop;
+}
+
+void Pbap::refreshContacts(int max_entries)
+{
+ PbapMessage *tmsg = new PbapMessage();
+ QJsonObject parameter;
+
+ if (max_entries >= 0)
+ parameter.insert("max_entries", max_entries);
+
+ tmsg->createRequest("contacts", parameter);
+ m_mloop->sendMessage(tmsg);
+ tmsg->deleteLater();
+}
+
+void Pbap::refreshCalls(int max_entries)
+{
+ PbapMessage *tmsg = new PbapMessage();
+ QJsonObject parameter;
+
+ parameter.insert("list", "cch");
+ if (max_entries >= 0)
+ parameter.insert("max_entries", max_entries);
+
+ tmsg->createRequest("history", parameter);
+ m_mloop->sendMessage(tmsg);
+ tmsg->deleteLater();
+}
+
+void Pbap::search(QString number)
+{
+ PbapMessage *tmsg = new PbapMessage();
+ QJsonObject parameter;
+
+ if (!number.isEmpty())
+ parameter.insert("number", number);
+ parameter.insert("max_entries", 1);
+
+ tmsg->createRequest("search", parameter);
+ m_mloop->sendMessage(tmsg);
+ tmsg->deleteLater();
+}
+
+void Pbap::updateContacts(QString vcards)
+{
+ QString name, number, type;
+
+ QList<vCard> contacts_vcards = vCard::fromByteArray(vcards.toUtf8());
+
+ for (auto vcard : contacts_vcards) {
+ vCardProperty name_prop = vcard.property(VC_FORMATTED_NAME);
+ QStringList values = name_prop.values();
+ name = values.at(vCardProperty::DefaultValue);
+ /*
+ * libvcard has no member function to return a list of named
+ * properties, so we iterate over all properties and parse
+ * each identified VC_TELEPHONE property in the vCard.
+ */
+ QList<QObject *> numbers;
+ vCardPropertyList properties = vcard.properties();
+ for (auto property : properties) {
+ if (property.isValid() && (property.name() == VC_TELEPHONE)) {
+ QStringList values = property.values();
+ number = values.at(0);
+ vCardParamList params = property.params();
+ // The first parameter is always the phone number type
+ type = params.at(0).value();
+ numbers.append(new PhoneNumber(number, type));
+ }
+ }
+ m_contacts.append(new Contact(name, numbers));
+ }
+ refreshCalls(100);
+}
+
+#define VC_DATETIME "X-IRMC-CALL-DATETIME"
+
+void Pbap::updateCalls(QString vcards)
+{
+ QString name, number, datetime, type;
+
+ QList<vCard> history_vcards = vCard::fromByteArray(vcards.toUtf8());
+
+ for (auto vcard : history_vcards) {
+ vCardProperty number_prop = vcard.property(VC_TELEPHONE);
+ if (number_prop.isValid()) {
+ QStringList values = number_prop.values();
+ number = values.at(0);
+ }
+ vCardProperty name_prop = vcard.property(VC_FORMATTED_NAME);
+ QStringList values = name_prop.values();
+ name = values.at(0);
+ // For calls with an empty name, fetch the name from contacts
+ if (name.isEmpty()) {
+ bool found = false;
+ for (auto contact_obj : m_contacts) {
+ Contact *contact = qobject_cast<Contact *>(contact_obj);
+ QList<QObject *>numbers = contact->numbers();
+ for (auto number_obj : numbers) {
+ PhoneNumber *phone_number = qobject_cast<PhoneNumber *>(number_obj);
+ if (number.endsWith(phone_number->number())) {
+ name = contact->name();
+ found = true;
+ break;
+ }
+ }
+ if (found == true)
+ break;
+ }
+ if (!found)
+ name = number;
+ }
+ vCardProperty datetime_prop = vcard.property(VC_DATETIME);
+ if (datetime_prop.isValid()) {
+ vCardParamList params = datetime_prop.params();
+ QStringList values = datetime_prop.values();
+ type = params.at(0).value();
+ datetime = values.at(0);
+ // Convert the PBAP date/time to ISO 8601 format
+ datetime.insert(4, '-');
+ datetime.insert(7, '-');
+ datetime.insert(13, ':');
+ datetime.insert(16, ':');
+ }
+ m_calls.append(new RecentCall(name, number, datetime, type));
+ }
+
+ // Refresh model
+ m_context->setContextProperty("RecentCallModel", QVariant::fromValue(m_calls));
+}
+
+void Pbap::sendSearchResults(QJsonArray results)
+{
+ QString name;
+
+ if (results.empty())
+ name = "Not Found";
+ else
+ name = results.at(0).toObject().value("name").toString();
+
+ emit searchResults(name);
+}
+
+void Pbap::onConnected()
+{
+ QStringListIterator eventIterator(events);
+ PbapMessage *tmsg;
+
+ while (eventIterator.hasNext()) {
+ tmsg = new PbapMessage();
+ QJsonObject parameter;
+ parameter.insert("value", eventIterator.next());
+ tmsg->createRequest("subscribe", parameter);
+ m_mloop->sendMessage(tmsg);
+ tmsg->deleteLater();
+ }
+}
+
+void Pbap::onDisconnected()
+{
+ QStringListIterator eventIterator(events);
+ PbapMessage *tmsg;
+
+ while (eventIterator.hasNext()) {
+ tmsg = new PbapMessage();
+ QJsonObject parameter;
+ parameter.insert("value", eventIterator.next());
+ tmsg->createRequest("unsubscribe", parameter);
+ m_mloop->sendMessage(tmsg);
+ tmsg->deleteLater();
+ }
+}
+
+void Pbap::onMessageReceived(MessageType type, Message *msg)
+{
+ if (msg->isEvent() && type == PbapEventMessage) {
+ PbapMessage *tmsg = qobject_cast<PbapMessage*>(msg);
+
+ if (tmsg->isStatusEvent()) {
+ emit statusChanged(tmsg->connected());
+ if (tmsg->connected() == true) {
+ refreshContacts(-1);
+ }
+ }
+ } else if (msg->isReply() && type == ResponseRequestMessage) {
+ ResponseMessage *tmsg = qobject_cast<ResponseMessage*>(msg);
+
+ if (tmsg->requestVerb() == "contacts") {
+ updateContacts(tmsg->replyData().value("vcards").toString());
+ } else if (tmsg->requestVerb() == "history") {
+ updateCalls(tmsg->replyData().value("vcards").toString());
+ } else if (tmsg->requestVerb() == "search") {
+ sendSearchResults(tmsg->replyData().value("results").toArray());
+ }
+ }
+
+ msg->deleteLater();
+}
diff --git a/pbap/pbap.h b/pbap/pbap.h
new file mode 100644
index 0000000..2a5fb61
--- /dev/null
+++ b/pbap/pbap.h
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2018 Konsulko Group
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef PBAP_H
+#define PBAP_H
+
+#include <QDebug>
+#include <QObject>
+#include <QJsonArray>
+#include <QtQml/QQmlContext>
+
+#include "messageengine.h"
+
+class PhoneNumber : public QObject
+{
+ Q_OBJECT
+
+ Q_PROPERTY(QString number READ number NOTIFY numberChanged)
+ Q_PROPERTY(int type READ type NOTIFY typeChanged)
+
+ public:
+ explicit PhoneNumber(QString number, QString type);
+ virtual ~PhoneNumber();
+
+ QString number() {return m_number;};
+ int type() {return m_type;};
+
+ enum PhoneNumberType {
+ UNKNOWN,
+ VOICE,
+ CELL,
+ HOME,
+ WORK,
+ FAX,
+ };
+ Q_ENUM(PhoneNumberType)
+
+ signals:
+ void numberChanged();
+ void typeChanged();
+
+ private:
+ QString m_number;
+ int m_type;
+ int stringToEnum(QString);
+};
+
+class Contact : public QObject
+{
+ Q_OBJECT
+
+ Q_PROPERTY(QString name READ name NOTIFY nameChanged)
+ Q_PROPERTY(QList<QObject *>numbers READ numbers NOTIFY numbersChanged)
+
+ public:
+ explicit Contact(QString name, QList<QObject *>numbers);
+ virtual ~Contact();
+
+ QString name() {return m_name;};
+ QList<QObject *>numbers() {return m_numbers;};
+
+ signals:
+ void nameChanged();
+ void numbersChanged();
+
+ private:
+ QString m_name;
+ QList<QObject *>m_numbers;
+};
+
+class RecentCall : public QObject
+{
+ Q_OBJECT
+
+ Q_PROPERTY(QString name READ name NOTIFY nameChanged)
+ Q_PROPERTY(QString number READ number NOTIFY numberChanged)
+ Q_PROPERTY(QString datetime READ datetime NOTIFY datetimeChanged)
+ Q_PROPERTY(int type READ type NOTIFY typeChanged)
+
+ public:
+ explicit RecentCall(QString name, QString number, QString datetime, QString type);
+ virtual ~RecentCall();
+
+ QString name() {return m_name;};
+ QString number() {return m_number;};
+ QString datetime() {return m_datetime;};
+ int type() {return m_type;};
+
+ enum RecentCallType {
+ UNKNOWN,
+ MISSED,
+ RECEIVED,
+ DIALED,
+ };
+ Q_ENUM(RecentCallType)
+
+ signals:
+ void nameChanged();
+ void numberChanged();
+ void datetimeChanged();
+ void typeChanged();
+
+ private:
+ QString m_name;
+ QString m_number;
+ QString m_datetime;
+ int m_type;
+ int stringToEnum(QString);
+};
+
+class Pbap : public QObject
+{
+ Q_OBJECT
+
+ public:
+ explicit Pbap(QUrl &url, QQmlContext *context, QObject * parent = Q_NULLPTR);
+ virtual ~Pbap();
+
+ Q_INVOKABLE void refreshContacts(int max_entries);
+ Q_INVOKABLE void refreshCalls(int max_entries);
+ Q_INVOKABLE void search(QString number);
+
+ signals:
+ void searchResults(QString name);
+ void statusChanged(bool connected);
+
+ private:
+ MessageEngine *m_mloop;
+ QQmlContext *m_context;
+ QList<QObject *>m_contacts;
+ QList<QObject *>m_calls;
+ void updateContacts(QString);
+ void updateCalls(QString);
+ void sendSearchResults(QJsonArray);
+
+ // slots
+ void onConnected();
+ void onDisconnected();
+ void onMessageReceived(MessageType, Message*);
+
+ const QStringList events {
+ "status",
+ };
+};
+
+#endif // PBAP_H
diff --git a/pbap/pbapmessage.cpp b/pbap/pbapmessage.cpp
new file mode 100644
index 0000000..751bb9f
--- /dev/null
+++ b/pbap/pbapmessage.cpp
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2018 Konsulko Group
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <QJsonArray>
+#include <QJsonDocument>
+#include <QJsonObject>
+
+#include "pbapmessage.h"
+
+bool PbapMessage::createRequest(QString verb, QJsonObject parameter)
+{
+ if (!verbs.contains(verb))
+ return false;
+
+ return Message::createRequest("bluetooth-pbap", verb, parameter);
+}
diff --git a/pbap/pbapmessage.h b/pbap/pbapmessage.h
new file mode 100644
index 0000000..65751cb
--- /dev/null
+++ b/pbap/pbapmessage.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2018 Konsulko Group
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef PBAP_MESSAGE_H
+#define PBAP_MESSAGE_H
+
+#include "message.h"
+
+class PbapMessage : public Message
+{
+ Q_OBJECT
+ public:
+ bool createRequest(QString verb, QJsonObject parameter);
+ bool isStatusEvent() {return (this->eventName() == "status"); };
+ bool connected() { return m_event_data.find("connected").value().toBool(); };
+
+ private:
+ QStringList verbs {
+ "contacts",
+ "entry",
+ "history",
+ "search",
+ "status",
+ "subscribe",
+ "unsubscribe",
+ };
+};
+
+#endif // PBAP_MESSAGE_H