/* * Copyright (C) 2018-2020 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 #include #include #include #include "callmessage.h" #include "eventmessage.h" #include "responsemessage.h" #include "messagefactory.h" #include "messageengine.h" #include "messageenginefactory.h" #include "pbap.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, QString photo, QListnumbers) { m_name = name; m_photo = photo; 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 = MessageEngineFactory::getInstance().getMessageEngine(url); m_context = context; m_context->setContextProperty("ContactsModel", QVariant::fromValue(m_contacts)); qmlRegisterUncreatableType("PhoneNumber", 1, 0, "PhoneNumber", "Enum"); m_context->setContextProperty("RecentCallModel", QVariant::fromValue(m_calls)); qmlRegisterUncreatableType("RecentCall", 1, 0, "RecentCall", "Enum"); QObject::connect(m_mloop.get(), &MessageEngine::connected, this, &Pbap::onConnected); QObject::connect(m_mloop.get(), &MessageEngine::disconnected, this, &Pbap::onDisconnected); QObject::connect(m_mloop.get(), &MessageEngine::messageReceived, this, &Pbap::onMessageReceived); } Pbap::~Pbap() { } void Pbap::importContacts(int max_entries) { std::unique_ptr msg = MessageFactory::getInstance().createOutboundMessage(MessageId::Call); if (!msg) return; CallMessage* pmsg = static_cast(msg.get()); QJsonObject parameter; pmsg->createRequest("bluetooth-pbap", "import", parameter); m_mloop->sendMessage(std::move(msg)); } void Pbap::refreshContacts(int max_entries) { std::unique_ptr msg = MessageFactory::getInstance().createOutboundMessage(MessageId::Call); if (!msg) return; CallMessage* pmsg = static_cast(msg.get()); QJsonObject parameter; pmsg->createRequest("bluetooth-pbap", "contacts", parameter); m_mloop->sendMessage(std::move(msg)); } void Pbap::refreshCalls(int max_entries) { std::unique_ptr msg = MessageFactory::getInstance().createOutboundMessage(MessageId::Call); if (!msg) return; CallMessage* pmsg = static_cast(msg.get()); QJsonObject parameter; parameter.insert("list", "cch"); if (max_entries >= 0) parameter.insert("max_entries", max_entries); pmsg->createRequest("bluetooth-pbap", "history", parameter); m_mloop->sendMessage(std::move(msg)); } void Pbap::search(QString number) { std::unique_ptr msg = MessageFactory::getInstance().createOutboundMessage(MessageId::Call); if (!msg) return; CallMessage* pmsg = static_cast(msg.get()); QJsonObject parameter; if (!number.isEmpty()) parameter.insert("number", number); parameter.insert("max_entries", 1); pmsg->createRequest("bluetooth-pbap", "search", parameter); m_mloop->sendMessage(std::move(msg)); } bool compareContactPtr(QObject *a, QObject *b) { Contact *contactA = qobject_cast(a); Contact *contactB = qobject_cast(b); return (*contactA < *contactB); } void Pbap::updateContacts(QJsonArray vcards) { m_contacts.clear(); for (auto vcard : vcards) { QJsonObject entry = vcard.toObject(); QString image; QString name = entry.value("fn").toString(); QList numbers; for (auto number: entry.value("telephone").toArray()) { QString type = number.toObject().keys().at(0); QString tel = number.toObject().value(type).toString(); numbers.append(new PhoneNumber(tel, type)); } if (entry.contains("photo")) { QJsonObject photo = entry.value("photo").toObject(); QString mimetype = photo.value("mimetype").toString(); QString mimedata = photo.value("data").toString(); image = "data:" + mimetype + ";base64," + mimedata; } if (!numbers.isEmpty()) m_contacts.append(new Contact(name, image, numbers)); } std::sort(m_contacts.begin(), m_contacts.end(), compareContactPtr); // Refresh model m_context->setContextProperty("ContactsModel", QVariant::fromValue(m_contacts)); refreshCalls(100); } void Pbap::updateCalls(QJsonArray vcards) { QString name, number, datetime, type; m_calls.clear(); for (auto vcard : vcards) { QJsonObject entry = vcard.toObject(); name = entry.value("fn").toString(); number = entry.value("telephone").toString(); // 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); QList numbers = contact->numbers(); for (auto phone_number : numbers) { if (number.endsWith(phone_number->number())) { name = contact->name(); found = true; break; } } if (found == true) break; } if (!found) name = number; } type = entry.value("type").toString(); datetime = entry.value("timestamp").toString(); // 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); while (eventIterator.hasNext()) { std::unique_ptr msg = MessageFactory::getInstance().createOutboundMessage(MessageId::Call); if (!msg) return; CallMessage* pmsg = static_cast(msg.get()); QJsonObject parameter; parameter.insert("value", eventIterator.next()); pmsg->createRequest("bluetooth-pbap", "subscribe", parameter); m_mloop->sendMessage(std::move(msg)); } } void Pbap::onDisconnected() { QStringListIterator eventIterator(events); while (eventIterator.hasNext()) { std::unique_ptr msg = MessageFactory::getInstance().createOutboundMessage(MessageId::Call); if (!msg) return; CallMessage* pmsg = static_cast(msg.get()); QJsonObject parameter; parameter.insert("value", eventIterator.next()); pmsg->createRequest("bluetooth-pbap", "unsubscribe", parameter); m_mloop->sendMessage(std::move(msg)); } } void Pbap::onMessageReceived(std::shared_ptr msg) { if (!msg) return; if (msg->isEvent()) { std::shared_ptr emsg = std::static_pointer_cast(msg); QString ename = emsg->eventName(); QString eapi = emsg->eventApi(); QVariantMap data = emsg->eventData().toVariantMap(); if (eapi != "bluetooth-pbap") return; bool connected = data.find("connected").value().toBool(); if (ename == "status") { emit statusChanged(connected); if (connected) refreshContacts(-1); } } else if (msg->isReply()) { std::shared_ptr rmsg = std::static_pointer_cast(msg); QString api = rmsg->requestApi(); if (api != "bluetooth-pbap") return; QString verb = rmsg->requestVerb(); QJsonObject data = rmsg->replyData(); if (verb == "contacts" || verb == "import") { updateContacts(data.value("vcards").toArray()); } else if (verb == "history") { updateCalls(data.value("vcards").toArray()); } else if (verb == "search") { sendSearchResults(data.value("results").toArray()); } } }