summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--saltoqd/contactsmanager.cpp92
-rw-r--r--saltoqd/contactsmanager.h13
-rw-r--r--saltoqd/notificationmanager.cpp6
-rw-r--r--saltoqd/obexconnection.cpp8
-rw-r--r--saltoqd/org.nemomobile.voicecall.VoiceCall.xml69
-rw-r--r--saltoqd/saltoqd.pro2
-rw-r--r--saltoqd/toqmanager.cpp2
-rw-r--r--saltoqd/voicecallmanager.cpp198
-rw-r--r--saltoqd/voicecallmanager.h14
9 files changed, 365 insertions, 39 deletions
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<QContact> contacts = _contacts->contacts(QContactPhoneNumber::match(normPhoneNumber));
+
+ if (!contacts.isEmpty()) {
+ const QContact& contact = contacts.first();
+ result.found = true;
+ result.name = contact.detail<QContactDisplayLabel>().label();
+ result.favorite = contact.detail<QContactFavorite>().isFavorite();
+
+ for (const QContactPhoneNumber &phone : contact.details<QContactPhoneNumber>()) {
+ 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<QContactPhoneNumber>()) {
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 <QtContacts/QContactManager>
+#include <QtContacts/QContactPhoneNumber>
#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();
@@ -22,6 +32,9 @@ private slots:
void refresh();
private:
+ static QString typeForContactPhone(const QtContacts::QContactPhoneNumber &number);
+
+private:
ToqManager *_toq;
StorageManager *_storage;
QtContacts::QContactManager *_contacts;
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 @@
+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
+"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
+<node>
+ <interface name="org.nemomobile.voicecall.VoiceCall">
+ <property name="handlerId" type="s" access="read"/>
+ <property name="providerId" type="s" access="read"/>
+ <property name="status" type="i" access="read"/>
+ <property name="statusText" type="s" access="read"/>
+ <property name="lineId" type="s" access="read"/>
+ <property name="startedAt" type="((iii)(iiii)i)" access="read">
+ <annotation name="org.qtproject.QtDBus.QtTypeName" value="QDateTime"/>
+ </property>
+ <property name="duration" type="i" access="read"/>
+ <property name="isIncoming" type="b" access="read"/>
+ <property name="isEmergency" type="b" access="read"/>
+ <property name="isMultiparty" type="b" access="read"/>
+ <property name="isForwarded" type="b" access="read"/>
+ <signal name="error">
+ <arg name="message" type="s" direction="out"/>
+ </signal>
+ <signal name="statusChanged">
+ <arg type="i" direction="out"/>
+ <arg type="s" direction="out"/>
+ </signal>
+ <signal name="lineIdChanged">
+ <arg type="s" direction="out"/>
+ </signal>
+ <signal name="startedAtChanged">
+ <arg type="((iii)(iiii)i)" direction="out"/>
+ <!-- For some reason both directions are seemingly required here -->
+ <annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QDateTime"/>
+ <annotation name="org.qtproject.QtDBus.QtTypeName.In0" value="QDateTime"/>
+ </signal>
+ <signal name="durationChanged">
+ <arg type="i" direction="out"/>
+ </signal>
+ <signal name="emergencyChanged">
+ <arg type="b" direction="out"/>
+ </signal>
+ <signal name="multipartyChanged">
+ <arg type="b" direction="out"/>
+ </signal>
+ <signal name="forwardedChanged">
+ <arg type="b" direction="out"/>
+ </signal>
+ <method name="answer">
+ <arg type="b" direction="out"/>
+ </method>
+ <method name="hangup">
+ <arg type="b" direction="out"/>
+ </method>
+ <method name="hold">
+ <arg type="b" direction="out"/>
+ <arg name="on" type="b" direction="in"/>
+ </method>
+ <method name="deflect">
+ <arg type="b" direction="out"/>
+ <arg name="target" type="s" direction="in"/>
+ </method>
+ <method name="sendDtmf">
+ <arg name="tones" type="s" direction="in"/>
+ </method>
+ <method name="getProperties">
+ <arg type="a{sv}" direction="out"/>
+ <annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QVariantMap"/>
+ </method>
+ </interface>
+</node>
+
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 <QtDBus/QDBusConnection>
+#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<ProfileValue>();
@@ -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;