From e8b328e0516d8ead9af13c8379a6cd1dbbb8a9e9 Mon Sep 17 00:00:00 2001 From: Matt Porter Date: Tue, 19 Jun 2018 14:56:26 -0400 Subject: pbap: add contacts support and recent call model Add support for parsing contacts and recent call data from the PBAP-provided vCards using libvcard. Expose a recent call list model and interfaces to refresh the data. Bug-AGL: SPEC-1436 Change-Id: Ia4a02443e22e4a27dc974ef414b765667b26ff83 Signed-off-by: Matt Porter --- pbap.cpp | 227 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 224 insertions(+), 3 deletions(-) (limited to 'pbap.cpp') diff --git a/pbap.cpp b/pbap.cpp index a833b9f..43db8bd 100644 --- a/pbap.cpp +++ b/pbap.cpp @@ -14,17 +14,80 @@ * limitations under the License. */ +#include +#include + +#include + #include "message.h" #include "messageengine.h" #include "pbap.h" #include "pbapmessage.h" #include "responsemessage.h" -Pbap::Pbap (QUrl &url, QObject * parent) : +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, QListnumbers) +{ + 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", 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); } @@ -33,6 +96,33 @@ 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(); @@ -47,6 +137,94 @@ void Pbap::search(QString number) tmsg->deleteLater(); } +void Pbap::updateContacts(QString vcards) +{ + QString name, number, type; + + QList 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 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 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_obj); + QListnumbers = contact->numbers(); + for (auto number_obj : numbers) { + PhoneNumber *phone_number = qobject_cast(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; @@ -59,12 +237,55 @@ void Pbap::sendSearchResults(QJsonArray results) 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->isReply() && type == ResponseRequestMessage) { + if (msg->isEvent() && type == PbapEventMessage) { + PbapMessage *tmsg = qobject_cast(msg); + + if (tmsg->isStatusEvent()) { + emit statusChanged(tmsg->connected()); + if (tmsg->connected() == true) { + refreshContacts(-1); + } + } + } else if (msg->isReply() && type == ResponseRequestMessage) { ResponseMessage *tmsg = qobject_cast(msg); - if (tmsg->requestVerb() == "search") { + 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()); } } -- cgit 1.2.3-korg