From ebc496d4e8038834e68ef2069bc53a0524da2008 Mon Sep 17 00:00:00 2001 From: Javier Date: Tue, 31 Mar 2015 17:33:29 +0200 Subject: implement the voicecallmanager (dialer, etc.) --- saltoqd/contactsmanager.cpp | 92 +++++++++--- saltoqd/contactsmanager.h | 13 ++ saltoqd/notificationmanager.cpp | 6 + saltoqd/obexconnection.cpp | 8 + saltoqd/org.nemomobile.voicecall.VoiceCall.xml | 69 +++++++++ saltoqd/saltoqd.pro | 2 +- saltoqd/toqmanager.cpp | 2 +- saltoqd/voicecallmanager.cpp | 198 +++++++++++++++++++++++-- saltoqd/voicecallmanager.h | 14 +- 9 files changed, 365 insertions(+), 39 deletions(-) create mode 100644 saltoqd/org.nemomobile.voicecall.VoiceCall.xml (limited to 'saltoqd') diff --git a/saltoqd/contactsmanager.cpp b/saltoqd/contactsmanager.cpp index 19e3c84..50b57a5 100644 --- a/saltoqd/contactsmanager.cpp +++ b/saltoqd/contactsmanager.cpp @@ -27,6 +27,49 @@ qint64 ContactsManager::getRecordIdForContact(uint contactId) return contactId; } +ContactsManager::NameType ContactsManager::findNameTypeForPhoneNumber(const QString &phoneNumber) const +{ + NameType result; + + result.found = false; + result.favorite = false; + + if (!phoneNumber.isEmpty()) { + const int maxCharacters = QtContactsSqliteExtensions::DefaultMaximumPhoneNumberCharacters; + QString normPhoneNumber = QtContactsSqliteExtensions::minimizePhoneNumber(phoneNumber, maxCharacters); + QList contacts = _contacts->contacts(QContactPhoneNumber::match(normPhoneNumber)); + + if (!contacts.isEmpty()) { + const QContact& contact = contacts.first(); + result.found = true; + result.name = contact.detail().label(); + result.favorite = contact.detail().isFavorite(); + + for (const QContactPhoneNumber &phone : contact.details()) { + if (phone.number() == normPhoneNumber) { + result.type = typeForContactPhone(phone); + break; + } + } + } else { + result.found = false; + } + } + + if (result.name.isEmpty()) { + if (phoneNumber.isEmpty()) { + result.name = tr("Unknown"); + } else { + result.name = phoneNumber; + } + } + if (result.type.isEmpty()) { + result.type = "Other"; + } + + return result; +} + void ContactsManager::scheduleRefresh() { if (!_refreshTimer->isActive()) { @@ -60,28 +103,7 @@ void ContactsManager::refresh() QJsonArray phones; for (const QContactPhoneNumber &cPhone : contact.details()) { QJsonObject phone; - QString type; - if (!cPhone.subTypes().isEmpty()) { - if (cPhone.subTypes().first() == QContactPhoneNumber::SubTypeMobile) { - type = "Mobile"; - } - } - if (type.isEmpty() && !cPhone.contexts().isEmpty()) { - switch (cPhone.contexts().first()) { - case QContactDetail::ContextHome: - type = "Home"; - break; - case QContactDetail::ContextWork: - type = "Work"; - break; - default: - break; - } - } - if (type.isEmpty()) { - type = "Other"; - } - phone.insert("Type", type); + phone.insert("Type", typeForContactPhone(cPhone)); phone.insert("Number", cPhone.number()); phones.append(phone); } @@ -108,3 +130,29 @@ void ContactsManager::refresh() emit changed(); } + +QString ContactsManager::typeForContactPhone(const QContactPhoneNumber &number) +{ + QString type; + if (!number.subTypes().isEmpty()) { + if (number.subTypes().first() == QContactPhoneNumber::SubTypeMobile) { + type = "Mobile"; + } + } + if (type.isEmpty() && !number.contexts().isEmpty()) { + switch (number.contexts().first()) { + case QContactDetail::ContextHome: + type = "Home"; + break; + case QContactDetail::ContextWork: + type = "Work"; + break; + default: + break; + } + } + if (type.isEmpty()) { + type = "Other"; + } + return type; +} diff --git a/saltoqd/contactsmanager.h b/saltoqd/contactsmanager.h index 0b39d40..d8c293f 100644 --- a/saltoqd/contactsmanager.h +++ b/saltoqd/contactsmanager.h @@ -2,6 +2,7 @@ #define CONTACTSMANAGER_H #include +#include #include "storagemanager.h" class ContactsManager : public QObject @@ -12,6 +13,15 @@ public: qint64 getRecordIdForContact(uint contactId); + struct NameType { + bool found; + QString name; + QString type; + bool favorite; + }; + + NameType findNameTypeForPhoneNumber(const QString &phoneNumber) const; + public slots: void scheduleRefresh(); @@ -21,6 +31,9 @@ signals: private slots: void refresh(); +private: + static QString typeForContactPhone(const QtContacts::QContactPhoneNumber &number); + private: ToqManager *_toq; StorageManager *_storage; diff --git a/saltoqd/notificationmanager.cpp b/saltoqd/notificationmanager.cpp index c5a4e88..30414ce 100644 --- a/saltoqd/notificationmanager.cpp +++ b/saltoqd/notificationmanager.cpp @@ -15,6 +15,12 @@ NotificationManager::NotificationManager(CardManager *card, ToqManager *toq) : void NotificationManager::handleNotification(MonitoredNotification *n) { uint notificationId = n->id(); + + if (n->sender().isEmpty() || (n->body().isEmpty() && n->summary().isEmpty())) { + // Never create a card for an empty notification + return; + } + Card *card = new Card(QString::number(qint64(notificationId))); card->setHeader(n->sender()); diff --git a/saltoqd/obexconnection.cpp b/saltoqd/obexconnection.cpp index 2e1da56..4b34dc3 100644 --- a/saltoqd/obexconnection.cpp +++ b/saltoqd/obexconnection.cpp @@ -86,6 +86,14 @@ void ObexConnection::tryConnect() void ObexConnection::handleToqConnected() { + if (_socket) { + qDebug() << "Toq just connected but OBEX connection still active"; + _socket->disconnectFromService(); + if (_socket) { + _socket->deleteLater(); + _socket = 0; + } + } tryConnect(); } diff --git a/saltoqd/org.nemomobile.voicecall.VoiceCall.xml b/saltoqd/org.nemomobile.voicecall.VoiceCall.xml new file mode 100644 index 0000000..6693af2 --- /dev/null +++ b/saltoqd/org.nemomobile.voicecall.VoiceCall.xml @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/saltoqd/saltoqd.pro b/saltoqd/saltoqd.pro index a454938..7e3a324 100644 --- a/saltoqd/saltoqd.pro +++ b/saltoqd/saltoqd.pro @@ -42,7 +42,7 @@ HEADERS += \ DBUS_ADAPTORS += org.freedesktop.Notifications.xml -DBUS_INTERFACES += com.nokia.profiled.xml org.nemomobile.voicecall.VoiceCallManager.xml +DBUS_INTERFACES += com.nokia.profiled.xml org.nemomobile.voicecall.VoiceCallManager.xml org.nemomobile.voicecall.VoiceCall.xml QDBUSXML2CPP_INTERFACE_HEADER_FLAGS = -i voicecallmanager.h target.path = /usr/bin diff --git a/saltoqd/toqmanager.cpp b/saltoqd/toqmanager.cpp index d7bef9c..104551e 100644 --- a/saltoqd/toqmanager.cpp +++ b/saltoqd/toqmanager.cpp @@ -27,7 +27,7 @@ ToqManager::ToqManager(MDConfGroup *settings, QObject *parent) : _musicManager(new MusicManager(this)), _contactsManager(new ContactsManager(_storageManager, this)), _commManager(new CommManager(_settings, _storageManager, _contactsManager, this)), - _voiceCallManager(new VoiceCallManager(this)), + _voiceCallManager(new VoiceCallManager(_contactsManager, this)), _weatherManager(new WeatherManager(_fmsManager, this)), _cardManager(new CardManager(_fmsManager, this)), _notificationManager(new NotificationManager(_cardManager, this)) diff --git a/saltoqd/voicecallmanager.cpp b/saltoqd/voicecallmanager.cpp index d2c5c52..79cfcf6 100644 --- a/saltoqd/voicecallmanager.cpp +++ b/saltoqd/voicecallmanager.cpp @@ -1,13 +1,27 @@ #include +#include "contactsmanager.h" #include "voicecallmanager.h" #include "voicecallmanager_interface.h" +#include "voicecall_interface.h" #include "profiled_interface.h" static ComNokiaProfiledInterface *profiled = NULL; static OrgNemomobileVoicecallVoiceCallManagerInterface *vcm = NULL; -VoiceCallManager::VoiceCallManager(ToqManager *toq) : - QObject(toq), _toq(toq) +enum VoiceCallStatus { + STATUS_NULL, + STATUS_ACTIVE, + STATUS_HELD, + STATUS_DIALING, + STATUS_ALERTING, + STATUS_INCOMING, + STATUS_WAITING, + STATUS_DISCONNECTED +}; + +VoiceCallManager::VoiceCallManager(ContactsManager *contacts, ToqManager *toq) : + QObject(toq), _toq(toq), _contacts(contacts), + _activeCall(0) { if (!profiled) { qDBusRegisterMetaType(); @@ -25,6 +39,8 @@ VoiceCallManager::VoiceCallManager(ToqManager *toq) : connect(profiled, &ComNokiaProfiledInterface::profile_changed, this, &VoiceCallManager::handleProfileChanged); + connect(vcm, &OrgNemomobileVoicecallVoiceCallManagerInterface::activeVoiceCallChanged, + this, &VoiceCallManager::handleActiveVoiceCallChanged); _toq->setEndpointListener(ToqConnection::VoiceCallEndpoint, this); } @@ -33,8 +49,69 @@ void VoiceCallManager::handleMessage(const ToqConnection::Message &msg) { Q_ASSERT(msg.destination == ToqConnection::VoiceCallEndpoint); switch (msg.type) { - case 15: - handleGetPhoneStatusMessage(msg); + case 0: // Call number? + if (vcm) { + QStringList providers = vcm->providers(); + if (providers.isEmpty()) { + sendReply(msg, 3, "No service"); + return; + } + QString provider = providers.first().split(":").at(0); + QString number = msg.toJson().object().value("dial_number").toString(); + if (vcm->dial(provider, number)) { + sendReply(msg, 0, "Succesfully dialed"); + } else { + sendReply(msg, 1, "Wrong number"); + } + } + break; + case 1: // Redial? + // TODO + sendReply(msg, 1, "Could not redial"); + break; + case 4: // Cancel call + if (_activeCall && _activeCall->hangup()) { + sendReply(msg, 0, "Succesfully ended"); + } else { + sendReply(msg, 1, "Could not cancel call"); + } + break; + case 5: // Answer call + if (_activeCall && _activeCall->answer()) { + sendReply(msg, 0, "Succesfully answered"); + } else { + sendReply(msg, 1, "Could not answer call"); + } + break; + case 6: // Reject call + if (_activeCall && _activeCall->hangup()) { + sendReply(msg, 0, "Succesfully rejected"); + } else { + sendReply(msg, 1, "Could not reject call"); + } + break; + case 7: // End call + if (_activeCall && _activeCall->hangup()) { + sendReply(msg, 0, "Succesfully ended"); + } else { + sendReply(msg, 1, "Could not end call"); + } + break; + case 9: // Speaker volume up + case 10: // Speaker volume down + // TODO + sendReply(msg, 1, "TODO"); + break; + case 11: // Mic mute + vcm->setIsMicrophoneMuted(true); + sendReply(msg, 0, "Succesfully muted"); + break; + case 12: // Mic unmute + vcm->setIsMicrophoneMuted(false); + sendReply(msg, 0, "Succesfully unmuted"); + break; + case 15: // Phone status + _toq->sendReply(msg, 0x400F, buildPhoneStatus()); break; default: qWarning() << "Unknown message" << msg.type; @@ -47,9 +124,12 @@ void VoiceCallManager::setSilentMode(bool silent) setProfile(silent ? "silent" : "ambience"); } -void VoiceCallManager::handleGetPhoneStatusMessage(const ToqConnection::Message &msg) +void VoiceCallManager::sendReply(const ToqConnection::Message &msg, int status, const QString &message) { - _toq->sendReply(msg, 0x400F, buildPhoneStatus()); + QJsonObject obj; + obj.insert("result", status); + obj.insert("description", message); + _toq->sendReply(msg, 0x4000 | msg.type, obj); } QString VoiceCallManager::getCurrentProfile() @@ -73,20 +153,85 @@ void VoiceCallManager::setProfile(const QString &name) } } +QJsonObject VoiceCallManager::buildPhoneStatus() +{ + QJsonObject obj; + obj.insert("service", int(1)); // TODO + int call_status = 0; + int call_setup_status = 0; + int call_held = 0; + if (_activeCall) { + switch (_activeCall->status()) { + case STATUS_ACTIVE: + call_status = 1; + break; + case STATUS_HELD: + call_status = 1; + call_held = 1; + break; + case STATUS_DIALING: + case STATUS_ALERTING: + call_status = 1; + break; + case STATUS_INCOMING: + call_status = 0; + call_setup_status = 1; + break; + case STATUS_WAITING: + call_status = 1; + call_setup_status = 0; + break; + case STATUS_NULL: + case STATUS_DISCONNECTED: + default: + call_status = 0; + call_setup_status = 0; + break; + } + + QString phoneNumber = _activeCall->lineId(); + auto callerId = _contacts->findNameTypeForPhoneNumber(phoneNumber); + obj.insert("caller_id", phoneNumber); + obj.insert("caller_name", callerId.name); + obj.insert("caller_phone_type", callerId.type); + obj.insert("privileged", callerId.favorite); + QDateTime callTime = _activeCall->startedAt(); + if (callTime.isValid()) { + obj.insert("call_time", qint64(_activeCall->startedAt().toTime_t())); + } + } + + obj.insert("call_status", call_status); + obj.insert("call_setup_status", call_setup_status); + obj.insert("call_held", call_held); + obj.insert("silence_mode", getCurrentProfile() == "silent" ? 1 : 0); + obj.insert("mic_mute", vcm->isMicrophoneMuted() ? 1 : 0); + + return obj; +} + void VoiceCallManager::sendPhoneStatusMessage() { _toq->sendMessage(ToqConnection::VoiceCallEndpoint, ToqConnection::VoiceCallEndpoint + 1, 0x8000, buildPhoneStatus()); } -QJsonObject VoiceCallManager::buildPhoneStatus() +void VoiceCallManager::sendPhoneRingMessage() { QJsonObject obj; - obj.insert("service", int(1)); - obj.insert("call_status", int(0)); - obj.insert("call_setup_status", int(0)); - obj.insert("silence_mode", getCurrentProfile() == "silent" ? 1 : 0); - return obj; + + if (_activeCall) { + QString phoneNumber = _activeCall->lineId(); + auto callerId = _contacts->findNameTypeForPhoneNumber(phoneNumber); + obj.insert("caller_id_present", phoneNumber.isEmpty() ? 0 : 1); + obj.insert("caller_id", phoneNumber); + obj.insert("caller_name", callerId.name); + obj.insert("caller_phone_type", callerId.type); + obj.insert("privileged", callerId.favorite); + } + + _toq->sendMessage(ToqConnection::VoiceCallEndpoint, ToqConnection::VoiceCallEndpoint + 1, + 0x8001, obj); } void VoiceCallManager::handleProfileChanged(bool changed, bool active, const QString &profile) @@ -97,6 +242,35 @@ void VoiceCallManager::handleProfileChanged(bool changed, bool active, const QSt sendPhoneStatusMessage(); } +void VoiceCallManager::handleActiveVoiceCallChanged() +{ + if (_activeCall) { + delete _activeCall; + } + QString id = vcm->activeVoiceCall(); + if (!id.isEmpty()) { + _activeCall = new OrgNemomobileVoicecallVoiceCallInterface("org.nemomobile.voicecall", + QString("/calls/%1").arg(id), + vcm->connection(), + this); + connect(_activeCall, &OrgNemomobileVoicecallVoiceCallInterface::statusChanged, + this, &VoiceCallManager::handleActiveVoiceCallStatusChanged); + } else { + _activeCall = 0; + } + sendPhoneStatusMessage(); +} + +void VoiceCallManager::handleActiveVoiceCallStatusChanged() +{ + int status = _activeCall->status(); + qDebug() << "Status changed" << status << _activeCall->statusText(); + if (status == STATUS_INCOMING) { + sendPhoneRingMessage(); + } + sendPhoneStatusMessage(); +} + QDBusArgument &operator<<(QDBusArgument &argument, const ProfileValue &value) { argument.beginStructure(); diff --git a/saltoqd/voicecallmanager.h b/saltoqd/voicecallmanager.h index 2e6275a..8bd2f5c 100644 --- a/saltoqd/voicecallmanager.h +++ b/saltoqd/voicecallmanager.h @@ -9,30 +9,38 @@ struct ProfileValue { QString type; }; +class ContactsManager; +class OrgNemomobileVoicecallVoiceCallInterface; + class VoiceCallManager : public QObject, public ToqManager::EndpointHandler { Q_OBJECT public: - explicit VoiceCallManager(ToqManager *toq); + explicit VoiceCallManager(ContactsManager *contacts, ToqManager *toq); void handleMessage(const ToqConnection::Message &msg) Q_DECL_OVERRIDE; static void setSilentMode(bool silent); private: - void handleGetPhoneStatusMessage(const ToqConnection::Message &msg); + void sendReply(const ToqConnection::Message &msg, int status, const QString &message); static QString getCurrentProfile(); static void setProfile(const QString &name); - void sendPhoneStatusMessage(); QJsonObject buildPhoneStatus(); + void sendPhoneStatusMessage(); + void sendPhoneRingMessage(); private slots: void handleProfileChanged(bool changed, bool active, const QString &profile); + void handleActiveVoiceCallChanged(); + void handleActiveVoiceCallStatusChanged(); private: ToqManager *_toq; + ContactsManager *_contacts; + OrgNemomobileVoicecallVoiceCallInterface *_activeCall; }; class QDBusArgument; -- cgit v1.2.3