diff options
author | Matt Porter <mporter@konsulko.com> | 2018-06-25 11:01:11 -0400 |
---|---|---|
committer | Matt Porter <mporter@konsulko.com> | 2018-06-25 11:01:11 -0400 |
commit | 389b5994553fdae59d484068d542910efee79e9c (patch) | |
tree | d987b1aec2fed4da44e0b4776be4cea8dfde22be /pbap | |
parent | e8b328e0516d8ead9af13c8379a6cd1dbbb8a9e9 (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.txt | 2 | ||||
-rw-r--r-- | pbap/pbap.cpp | 294 | ||||
-rw-r--r-- | pbap/pbap.h | 159 | ||||
-rw-r--r-- | pbap/pbapmessage.cpp | 29 | ||||
-rw-r--r-- | pbap/pbapmessage.h | 42 |
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 |