summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJavier <dev.git@javispedro.com>2015-03-23 03:28:00 +0100
committerJavier <dev.git@javispedro.com>2015-03-23 03:28:00 +0100
commit5fef356ff3fbcb76a5ec44a81a8d54d29a42f25d (patch)
tree81eb5f54b3f2fba5b33ff46634628dd1a9b3fd5b
downloadsaltoq-5fef356ff3fbcb76a5ec44a81a8d54d29a42f25d.tar.gz
saltoq-5fef356ff3fbcb76a5ec44a81a8d54d29a42f25d.zip
initial import
-rw-r--r--rpm/saltoq.yaml41
-rw-r--r--saltoq.pro6
-rw-r--r--saltoqd/commmanager.cpp93
-rw-r--r--saltoqd/commmanager.h32
-rw-r--r--saltoqd/main.cpp12
-rw-r--r--saltoqd/musicmanager.cpp199
-rw-r--r--saltoqd/musicmanager.h39
-rw-r--r--saltoqd/notificationmanager.cpp7
-rw-r--r--saltoqd/notificationmanager.h16
-rw-r--r--saltoqd/notificationmonitor.cpp79
-rw-r--r--saltoqd/notificationmonitor.h28
-rw-r--r--saltoqd/org.freedesktop.Notifications.xml17
-rw-r--r--saltoqd/org.nemomobile.voicecall.VoiceCallManager.xml53
-rw-r--r--saltoqd/saltoqd.pro38
-rw-r--r--saltoqd/storagemanager.cpp17
-rw-r--r--saltoqd/storagemanager.h29
-rw-r--r--saltoqd/systemmanager.cpp55
-rw-r--r--saltoqd/systemmanager.h22
-rw-r--r--saltoqd/toqconnection.cpp187
-rw-r--r--saltoqd/toqconnection.h94
-rw-r--r--saltoqd/toqmanager.cpp73
-rw-r--r--saltoqd/toqmanager.h54
-rw-r--r--saltoqd/versionmanager.cpp32
-rw-r--r--saltoqd/versionmanager.h21
-rw-r--r--saltoqd/voicecallmanager.cpp73
-rw-r--r--saltoqd/voicecallmanager.h26
26 files changed, 1343 insertions, 0 deletions
diff --git a/rpm/saltoq.yaml b/rpm/saltoq.yaml
new file mode 100644
index 0000000..a90944b
--- /dev/null
+++ b/rpm/saltoq.yaml
@@ -0,0 +1,41 @@
+Name: saltoq
+Summary: Qualcomm Toq manager application
+Version: 0.0.1
+Release: 1
+Group: Communications/Bluetooth
+URL: https://gitorious.org/javispedro-jolla-misc/salmeta/
+License: GPLv3
+Sources:
+- '%{name}-%{version}.tar.bz2'
+Description: |
+ Qualcomm Toq.
+Configure: none
+Builder: qtc5
+
+PkgConfigBR:
+ - sailfishapp >= 1.0.2
+ - Qt5Core >= 5.2
+ - Qt5Qml
+ - Qt5Quick
+ - Qt5DBus
+ - Qt5Bluetooth >= 5.2
+ - mlite5
+ - libiphb
+
+PkgBR:
+# Workaround current sailfish qt5connectivity packaging bug
+ - qt5-qtconnectivity-qtbluetooth-devel
+
+Requires:
+ - sailfishsilica-qt5 >= 0.10.9
+ - sailfish-components-bluetooth-qt5
+ - nemo-qml-plugin-configuration-qt5
+ - systemd
+ - systemd-user-session-targets
+
+Files:
+ - '%{_bindir}'
+# - '%{_datadir}/%{name}'
+# - '%{_datadir}/applications/%{name}.desktop'
+# - '%{_datadir}/icons/hicolor/86x86/apps/%{name}.png'
+# - '%{_libdir}/systemd/user/salmeta.service'
diff --git a/saltoq.pro b/saltoq.pro
new file mode 100644
index 0000000..83f6021
--- /dev/null
+++ b/saltoq.pro
@@ -0,0 +1,6 @@
+TEMPLATE = subdirs
+
+SUBDIRS += \
+ saltoqd
+
+OTHER_FILES = rpm/saltoq.yaml rpm/saltoq.spec
diff --git a/saltoqd/commmanager.cpp b/saltoqd/commmanager.cpp
new file mode 100644
index 0000000..271749a
--- /dev/null
+++ b/saltoqd/commmanager.cpp
@@ -0,0 +1,93 @@
+#include "commmanager.h"
+
+using namespace CommHistory;
+
+CommManager::CommManager(StorageManager *storage, ToqManager *toq) :
+ QObject(toq), _toq(toq), _storage(storage),
+ _calls(new CallModel(this)),
+ _convs(new GroupModel(this)),
+ _refreshTimer(new QTimer(this))
+{
+ _calls->setTreeMode(false);
+ _calls->setQueryMode(EventModel::AsyncQuery);
+ _calls->setSorting(CallModel::SortByTime);
+ _calls->setResolveContacts(true);
+ _calls->setLimit(20);
+
+ _convs->setQueryMode(EventModel::AsyncQuery);
+ _convs->setLimit(20);
+
+ _refreshTimer->setSingleShot(true);
+ _refreshTimer->setInterval(1000);
+
+ connect(_calls, SIGNAL(modelReady(bool)),
+ this, SLOT(scheduleRefresh()));
+ connect(_calls, SIGNAL(modelReset()),
+ this, SLOT(scheduleRefresh()));
+ connect(_calls, SIGNAL(rowsInserted(QModelIndex,int,int)),
+ this, SLOT(scheduleRefresh()));
+ connect(_calls, SIGNAL(rowsRemoved(QModelIndex,int,int)),
+ this, SLOT(scheduleRefresh()));
+
+ connect(_convs, SIGNAL(modelReady(bool)),
+ this, SLOT(scheduleRefresh()));
+
+ connect(_refreshTimer, &QTimer::timeout,
+ this, &CommManager::refresh);
+
+ if (!_calls->getEvents()) {
+ qWarning() << "Could not get the call log";
+ }
+ if (!_convs->getGroups()) {
+ qWarning() << "Could not get conversation groups";
+ }
+}
+
+void CommManager::scheduleRefresh()
+{
+ if (!_refreshTimer->isActive()) {
+ _refreshTimer->start();
+ }
+}
+
+void CommManager::refresh()
+{
+ qDebug() << "refreshing now";
+ QMultiMap<QDateTime, QJsonObject> events;
+
+ int rows = _calls->rowCount();
+ for (int i = 0; i < rows; i++) {
+ Event e = _calls->event(i);
+ qDebug() << "Got call" << e.toString();
+ QDateTime dt = e.startTime();
+ QJsonObject obj;
+ obj.insert("CommsType", QLatin1String("Call"));
+ obj.insert("ReceivedTime", qint64(dt.toTime_t()));
+ obj.insert("CallerId", e.contactName());
+ obj.insert("ItemId", e.id());
+ QJsonObject details;
+ details.insert("Duration", e.startTime().secsTo(e.endTime()));
+ details.insert("PhoneType", QLatin1String("Other")); // TODO
+ switch (e.direction()) {
+ case Event::Inbound:
+ details.insert("Direction", QLatin1String("Incoming"));
+ break;
+ case Event::Outbound:
+ details.insert("Direction", QLatin1String("Outgoing"));
+ break;
+ default:
+ details.insert("Direction", QLatin1String("Unknown"));
+ break;
+ }
+ details.insert("IsMissedCall", e.isMissedCall());
+ obj.insert("CommsDetails", details);
+
+ events.insert(dt, obj);
+ }
+
+ rows = _convs->rowCount();
+ for (int i = 0; i < rows; i++) {
+ Group g = _convs->group(_convs->index(i, 0));
+ qDebug() << "Chat" << g.contactName();
+ }
+}
diff --git a/saltoqd/commmanager.h b/saltoqd/commmanager.h
new file mode 100644
index 0000000..05d2147
--- /dev/null
+++ b/saltoqd/commmanager.h
@@ -0,0 +1,32 @@
+#ifndef COMMMANAGER_H
+#define COMMMANAGER_H
+
+#include "storagemanager.h"
+#include <CommHistory/CallModel>
+#include <CommHistory/GroupModel>
+
+class CommManager : public QObject
+{
+ Q_OBJECT
+public:
+ explicit CommManager(StorageManager *storage, ToqManager *toq);
+
+public slots:
+ void scheduleRefresh();
+
+signals:
+
+private slots:
+ void refresh();
+
+private:
+ ToqManager *_toq;
+ StorageManager *_storage;
+
+ CommHistory::CallModel *_calls;
+ CommHistory::GroupModel *_convs;
+
+ QTimer *_refreshTimer;
+};
+
+#endif // COMMMANAGER_H
diff --git a/saltoqd/main.cpp b/saltoqd/main.cpp
new file mode 100644
index 0000000..1f34c0d
--- /dev/null
+++ b/saltoqd/main.cpp
@@ -0,0 +1,12 @@
+#include <QtCore>
+#include "toqmanager.h"
+
+int main(int argc, char **argv)
+{
+ QCoreApplication app(argc, argv);
+
+ QBluetoothAddress addr("64:9C:81:00:13:EF");
+ QScopedPointer<ToqManager> manager(new ToqManager(addr));
+
+ return app.exec();
+}
diff --git a/saltoqd/musicmanager.cpp b/saltoqd/musicmanager.cpp
new file mode 100644
index 0000000..8b1e9a9
--- /dev/null
+++ b/saltoqd/musicmanager.cpp
@@ -0,0 +1,199 @@
+#include <QtDBus/QDBusConnectionInterface>
+#include "musicmanager.h"
+
+MusicManager::MusicManager(ToqManager *toq) :
+ QObject(toq), _toq(toq), _watcher(new QDBusServiceWatcher(this)), _isPlayerVisible(false)
+{
+ QDBusConnection bus = QDBusConnection::sessionBus();
+ QDBusConnectionInterface *bus_iface = bus.interface();
+
+ // This watcher will be used to find when the current MPRIS service dies
+ // (and thus we must clear the metadata)
+ _watcher->setConnection(bus);
+ connect(_watcher, &QDBusServiceWatcher::serviceOwnerChanged,
+ this, &MusicManager::handleMprisServiceOwnerChanged);
+
+ // Try to find an active MPRIS service to initially connect to
+ const QStringList &services = bus_iface->registeredServiceNames();
+ foreach (QString service, services) {
+ if (service.startsWith("org.mpris.MediaPlayer2.")) {
+ switchToService(service);
+ fetchMetadataFromService();
+ // The watch is not connected by this point,
+ // so we don't send the current metadata.
+ break;
+ }
+ }
+
+ // Even if we didn't find any service, we still listen for metadataChanged signals
+ // from every MPRIS-compatible player
+ // If such a signal comes in, we will connect to the source service for that signal
+ bus.connect("", "/org/mpris/MediaPlayer2", "org.freedesktop.DBus.Properties", "PropertiesChanged",
+ this, SLOT(handleMprisPropertiesChanged(QString,QMap<QString,QVariant>,QStringList)));
+
+ _toq->setEndpointListener(ToqConnection::MusicEndpoint, this);
+}
+
+void MusicManager::handleMessage(const ToqConnection::Message &msg)
+{
+ Q_ASSERT(msg.destination == ToqConnection::MusicEndpoint);
+ switch (msg.type) {
+ case 0:
+ handleGetPlayerStatusMessage(msg);
+ _isPlayerVisible = true;
+ break;
+ case 1:
+ callMprisMethod("Play");
+ // TODO Reply with new metadata!
+ break;
+ case 2:
+ callMprisMethod("Pause");
+ break;
+ case 3:
+ callMprisMethod("Next");
+ break;
+ case 4:
+ callMprisMethod("Previous");
+ break;
+ case 0x8004: // Player closing?
+ _isPlayerVisible = false;
+ break;
+ case 5: //Volume up/down
+ case 6:
+ // Not yet implemented
+ default:
+ qWarning() << "Unknown message type" << msg.type;
+ break;
+ }
+}
+
+void MusicManager::handleGetPlayerStatusMessage(const ToqConnection::Message &msg)
+{
+ QJsonObject reply;
+ reply.insert("result", int(0));
+ reply.insert("description", QLatin1String("PlayerStatus Request received"));
+ reply.insert("artist", _curMetadata.value("xesam:artist").toString());
+ reply.insert("album", _curMetadata.value("xesam:album").toString());
+ reply.insert("title", _curMetadata.value("xesam:title").toString());
+ reply.insert("state", _curPlaybackStatus == "Playing" ? QLatin1String("playing") : QLatin1String("paused"));
+ reply.insert("album-art", QLatin1String(""));
+ reply.insert("playlist", int(1)); // TODO
+ reply.insert("player", _curService);
+ reply.insert("version", QLatin1String("1.0"));
+ reply.insert("controller", QLatin1String("SERIAL"));
+
+ _toq->sendReply(msg, 0x4000, reply);
+}
+
+void MusicManager::switchToService(const QString &service)
+{
+ if (_curService != service) {
+ qDebug() << "switching to mpris service" << service;
+ _curService = service;
+
+ if (_curService.isEmpty()) {
+ _watcher->setWatchedServices(QStringList());
+ } else {
+ _watcher->setWatchedServices(QStringList(_curService));
+ }
+ }
+}
+
+void MusicManager::fetchMetadataFromService()
+{
+ _curMetadata.clear();
+
+ if (!_curService.isEmpty()) {
+ QDBusMessage call = QDBusMessage::createMethodCall(_curService, "/org/mpris/MediaPlayer2", "org.freedesktop.DBus.Properties", "Get");
+ call << "org.mpris.MediaPlayer2.Player" << "Metadata";
+ QDBusReply<QDBusVariant> reply = QDBusConnection::sessionBus().call(call);
+ if (reply.isValid()) {
+ qDebug() << "got mpris metadata from service" << _curService;
+ _curMetadata = qdbus_cast<QVariantMap>(reply.value().variant().value<QDBusArgument>());
+ } else {
+ qWarning() << reply.error().message();
+ }
+ }
+}
+
+void MusicManager::sendCurrentMprisMetadata()
+{
+ Q_ASSERT(_toq->isConnected());
+
+ QJsonObject obj;
+ fillWithMetadata(obj);
+
+ _toq->sendMessage(ToqConnection::MusicEndpoint, ToqConnection::MusicEndpoint + 1, 0x8003, obj);
+}
+
+void MusicManager::fillWithMetadata(QJsonObject &obj)
+{
+ obj.insert("artist", _curMetadata.value("xesam:artist").toString());
+ obj.insert("album", _curMetadata.value("xesam:album").toString());
+ obj.insert("title", _curMetadata.value("xesam:title").toString());
+ obj.insert("state", _curPlaybackStatus == "Playing" ? QLatin1String("playing") : QLatin1String("paused"));
+ obj.insert("album-art", QLatin1String(""));
+ obj.insert("playlist", int(1)); // TODO
+ obj.insert("player", _curService);
+ obj.insert("version", QLatin1String("1.0"));
+ obj.insert("controller", QLatin1String("SERIAL"));
+}
+
+void MusicManager::callMprisMethod(const QString &method)
+{
+ Q_ASSERT(!method.isEmpty());
+ Q_ASSERT(!_curService.isEmpty());
+
+ qDebug() << _curService << "->" << method;
+
+ QDBusConnection bus = QDBusConnection::sessionBus();
+ QDBusMessage call = QDBusMessage::createMethodCall(_curService,
+ "/org/mpris/MediaPlayer2",
+ "org.mpris.MediaPlayer2.Player",
+ method);
+
+ QDBusError err = bus.call(call);
+
+ if (err.isValid()) {
+ qWarning() << "while calling mpris method on" << _curService << ":" << err.message();
+ }
+}
+
+void MusicManager::handleMprisServiceOwnerChanged(const QString &name, const QString &oldOwner, const QString &newOwner)
+{
+ Q_UNUSED(oldOwner);
+ if (name == _curService && newOwner.isEmpty()) {
+ // Oops, current service is going away
+ switchToService(QString());
+ _curMetadata.clear();
+ if (_toq->isConnected() && _isPlayerVisible) {
+ sendCurrentMprisMetadata();
+ }
+ }
+}
+
+void MusicManager::handleMprisPropertiesChanged(const QString &interface, const QMap<QString, QVariant> &changed, const QStringList &invalidated)
+{
+ Q_ASSERT(calledFromDBus());
+ Q_UNUSED(interface);
+ Q_UNUSED(invalidated);
+
+ if (changed.contains("Metadata")) {
+ QVariantMap metadata = qdbus_cast<QVariantMap>(changed.value("Metadata").value<QDBusArgument>());
+ qDebug() << "received new metadata" << metadata;
+ _curMetadata = metadata;
+ }
+
+ if (changed.contains("PlaybackStatus")) {
+ _curPlaybackStatus = changed.value("PlaybackStatus").toString();
+ if (_curPlaybackStatus == "Stopped") {
+ _curMetadata.clear();
+ }
+ }
+
+ if (_toq->isConnected() && _isPlayerVisible) {
+ sendCurrentMprisMetadata();
+ }
+
+ switchToService(message().service());
+}
diff --git a/saltoqd/musicmanager.h b/saltoqd/musicmanager.h
new file mode 100644
index 0000000..5bae89b
--- /dev/null
+++ b/saltoqd/musicmanager.h
@@ -0,0 +1,39 @@
+#ifndef MUSICMANAGER_H
+#define MUSICMANAGER_H
+
+#include <QtDBus/QDBusServiceWatcher>
+#include <QtDBus/QDBusContext>
+#include "toqmanager.h"
+
+class MusicManager : public QObject, public ToqManager::EndpointHandler, protected QDBusContext
+{
+ Q_OBJECT
+public:
+ explicit MusicManager(ToqManager *toq);
+
+ void handleMessage(const ToqConnection::Message &msg) Q_DECL_OVERRIDE;
+
+private:
+ void handleGetPlayerStatusMessage(const ToqConnection::Message &msg);
+
+ void switchToService(const QString &service);
+ void fetchMetadataFromService();
+ void sendCurrentMprisMetadata();
+ void fillWithMetadata(QJsonObject &obj);
+ void callMprisMethod(const QString &method);
+
+private slots:
+ void handleMprisServiceOwnerChanged(const QString &serviceName, const QString &oldOwner, const QString &newOwner);
+ void handleMprisPropertiesChanged(const QString &interface, const QMap<QString,QVariant> &changed, const QStringList &invalidated);
+
+private:
+ ToqManager *_toq;
+ QDBusServiceWatcher *_watcher;
+ QString _curService;
+ QVariantMap _curMetadata;
+ QString _curPlaybackStatus;
+ bool _isPlayerVisible;
+};
+
+
+#endif // MUSICMANAGER_H
diff --git a/saltoqd/notificationmanager.cpp b/saltoqd/notificationmanager.cpp
new file mode 100644
index 0000000..fb52785
--- /dev/null
+++ b/saltoqd/notificationmanager.cpp
@@ -0,0 +1,7 @@
+#include "notificationmanager.h"
+
+NotificationManager::NotificationManager(ToqManager *toq) :
+ QObject(toq), _toq(toq)
+{
+
+}
diff --git a/saltoqd/notificationmanager.h b/saltoqd/notificationmanager.h
new file mode 100644
index 0000000..1889336
--- /dev/null
+++ b/saltoqd/notificationmanager.h
@@ -0,0 +1,16 @@
+#ifndef NOTIFICATIONMANAGER_H
+#define NOTIFICATIONMANAGER_H
+
+#include "toqmanager.h"
+
+class NotificationManager : public QObject
+{
+ Q_OBJECT
+public:
+ explicit NotificationManager(ToqManager *toq);
+
+private:
+ ToqManager *_toq;
+};
+
+#endif // NOTIFICATIONMANAGER_H
diff --git a/saltoqd/notificationmonitor.cpp b/saltoqd/notificationmonitor.cpp
new file mode 100644
index 0000000..f22ce02
--- /dev/null
+++ b/saltoqd/notificationmonitor.cpp
@@ -0,0 +1,79 @@
+#include <QtCore/QDebug>
+#include <QtGui/QIcon>
+#include <QtDBus/QDBusConnection>
+#include <QtDBus/QDBusConnectionInterface>
+
+#include "notificationmonitor.h"
+#include "notifications_adaptor.h"
+
+static NotificationMonitor *global_monitor = 0;
+
+NotificationMonitor::NotificationMonitor(QObject *parent) :
+ QObject(parent)
+{
+ QDBusConnection bus = QDBusConnection::sessionBus();
+ QDBusConnectionInterface *dbus = bus.interface();
+ dbus->call("AddMatch",
+ "interface='org.freedesktop.Notifications',member='Notify',type='method_call',eavesdrop='true'");
+ new NotificationsAdaptor(this);
+ bus.registerObject("/org/freedesktop/Notifications", this);
+}
+
+NotificationMonitor::~NotificationMonitor()
+{
+ QDBusConnection bus = QDBusConnection::sessionBus();
+ QDBusConnectionInterface *dbus = bus.interface();
+ dbus->call("RemoveMatch",
+ "interface='org.freedesktop.Notifications',member='Notify',type='method_call',eavesdrop='true'");
+}
+
+NotificationMonitor *NotificationMonitor::instance()
+{
+ if (!global_monitor) {
+ global_monitor = new NotificationMonitor;
+ }
+ return global_monitor;
+}
+
+uint NotificationMonitor::Notify(const QString &app_name, uint replaces_id, const QString &app_icon, const QString &summary, const QString &body, const QStringList &actions, const QVariantHash &hints, int expire_timeout)
+{
+ QIcon icon;
+
+ qDebug() << "Got notification" << app_name << app_icon << summary << body;
+
+ // Avoid sending a reply for this method call, since we've received it
+ // because we're eavesdropping.
+ // The actual target of the method call will send the proper reply, not us.
+ Q_ASSERT(calledFromDBus());
+ setDelayedReply(true);
+
+ // If the notification mentions a specific icon, then use it.
+ // But otherwise let's prefer our builtin icons.
+ if (app_icon.startsWith("/")) {
+ icon = QIcon(app_icon);
+ } else if (app_icon.startsWith("file:")) {
+ QUrl url(app_icon);
+ icon = QIcon(url.toLocalFile());
+ } else {
+ QString category = hints.value("category").toString();
+ // Let's hardcode a few categories for now..
+ if (!category.isEmpty()) {
+ qDebug() << "TODO: Category icons";
+ }
+ }
+
+ int count = hints.value("x-nemo-item-count").toInt();
+ QDateTime dateTime = hints.value("x-nemo-timestamp").toDateTime();
+ if (!dateTime.isValid()) {
+ dateTime = QDateTime::currentDateTime();
+ }
+
+ if (summary.isEmpty() && body.isEmpty()) {
+ // Avoid sending empty notifications to watch.
+ return 0;
+ }
+
+ emit incomingNotification(app_name, icon, summary, count, body, dateTime);
+
+ return 0;
+}
diff --git a/saltoqd/notificationmonitor.h b/saltoqd/notificationmonitor.h
new file mode 100644
index 0000000..c0f7691
--- /dev/null
+++ b/saltoqd/notificationmonitor.h
@@ -0,0 +1,28 @@
+#ifndef NOTIFICATIONMONITOR_H
+#define NOTIFICATIONMONITOR_H
+
+#include <QtCore/QObject>
+#include <QtCore/QDateTime>
+#include <QtDBus/QDBusContext>
+
+class NotificationMonitor : public QObject, protected QDBusContext
+{
+ Q_OBJECT
+
+public:
+ ~NotificationMonitor();
+
+ static NotificationMonitor *instance();
+
+signals:
+ void incomingNotification(const QString &sender, const QIcon &icon, const QString &summary, int count, const QString &body, const QDateTime &dateTime);
+
+private:
+ explicit NotificationMonitor(QObject *parent = 0);
+
+private slots:
+ friend class NotificationsAdaptor;
+ uint Notify(const QString &app_name, uint replaces_id, const QString &app_icon, const QString &summary, const QString &body, const QStringList &actions, const QVariantHash &hints, int expire_timeout);
+};
+
+#endif // NOTIFICATIONMONITOR_H
diff --git a/saltoqd/org.freedesktop.Notifications.xml b/saltoqd/org.freedesktop.Notifications.xml
new file mode 100644
index 0000000..d3f8bef
--- /dev/null
+++ b/saltoqd/org.freedesktop.Notifications.xml
@@ -0,0 +1,17 @@
+<!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.freedesktop.Notifications">
+ <!-- (Partial) Desktop Notification Specification interface -->
+ <method name="Notify">
+ <arg name="app_name" type="s" direction="in"/>
+ <arg name="replaces_id" type="u" direction="in"/>
+ <arg name="app_icon" type="s" direction="in"/>
+ <arg name="summary" type="s" direction="in"/>
+ <arg name="body" type="s" direction="in"/>
+ <arg name="actions" type="as" direction="in"/>
+ <arg name="hints" type="a{sv}" direction="in"/>
+ <arg name="expire_timeout" type="i" direction="in"/>
+ <arg name="id" type="u" direction="out"/>
+ <annotation name="org.qtproject.QtDBus.QtTypeName.In6" value="QVariantHash"/>
+ </method>
+</node>
diff --git a/saltoqd/org.nemomobile.voicecall.VoiceCallManager.xml b/saltoqd/org.nemomobile.voicecall.VoiceCallManager.xml
new file mode 100644
index 0000000..cfdbae5
--- /dev/null
+++ b/saltoqd/org.nemomobile.voicecall.VoiceCallManager.xml
@@ -0,0 +1,53 @@
+<!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.VoiceCallManager">
+ <property name="providers" type="as" access="read"/>
+ <property name="voiceCalls" type="as" access="read"/>
+ <property name="activeVoiceCall" type="s" access="read"/>
+ <property name="audioMode" type="s" access="readwrite"/>
+ <property name="isAudioRouted" type="b" access="readwrite"/>
+ <property name="isMicrophoneMuted" type="b" access="readwrite"/>
+ <property name="isSpeakerMuted" type="b" access="readwrite"/>
+ <property name="totalOutgoingCallDuration" type="i" access="read"/>
+ <property name="totalIncomingCallDuration" type="i" access="read"/>
+ <signal name="error">
+ <arg name="message" type="s" direction="out"/>
+ </signal>
+ <signal name="providersChanged">
+ </signal>
+ <signal name="voiceCallsChanged">
+ </signal>
+ <signal name="activeVoiceCallChanged">
+ </signal>
+ <signal name="audioModeChanged">
+ </signal>
+ <signal name="audioRoutedChanged">
+ </signal>
+ <signal name="microphoneMutedChanged">
+ </signal>
+ <signal name="speakerMutedChanged">
+ </signal>
+ <signal name="totalOutgoingCallDurationChanged">
+ </signal>
+ <signal name="totalIncomingCallDurationChanged">
+ </signal>
+ <method name="dial">
+ <arg type="b" direction="out"/>
+ <arg name="provider" type="s" direction="in"/>
+ <arg name="msisdn" type="s" direction="in"/>
+ </method>
+ <method name="silenceRingtone">
+ </method>
+ <method name="startDtmfTone">
+ <arg type="b" direction="out"/>
+ <arg name="tone" type="s" direction="in"/>
+ </method>
+ <method name="stopDtmfTone">
+ <arg type="b" direction="out"/>
+ </method>
+ <method name="resetCallDurationCounters">
+ </method>
+ </interface>
+</node>
+
diff --git a/saltoqd/saltoqd.pro b/saltoqd/saltoqd.pro
new file mode 100644
index 0000000..2c50db5
--- /dev/null
+++ b/saltoqd/saltoqd.pro
@@ -0,0 +1,38 @@
+TEMPLATE = app
+CONFIG += console
+QT -= qml
+QT += dbus bluetooth
+
+CONFIG += c++11 link_pkgconfig
+
+PKGCONFIG += zlib commhistory-qt5
+INCLUDEPATH += /usr/include/commhistory-qt5
+
+SOURCES += main.cpp \
+ toqconnection.cpp \
+ toqmanager.cpp \
+ versionmanager.cpp \
+ systemmanager.cpp \
+ musicmanager.cpp \
+ storagemanager.cpp \
+ commmanager.cpp \
+ notificationmanager.cpp notificationmonitor.cpp \
+ voicecallmanager.cpp
+
+HEADERS += \
+ toqconnection.h \
+ toqmanager.h \
+ versionmanager.h \
+ systemmanager.h \
+ musicmanager.h \
+ storagemanager.h \
+ commmanager.h \
+ notificationmanager.h notificationmonitor.h \
+ voicecallmanager.h
+
+DBUS_ADAPTORS += org.freedesktop.Notifications.xml
+
+DBUS_INTERFACES += org.nemomobile.voicecall.VoiceCallManager.xml
+
+target.path = /usr/bin
+INSTALLS += target
diff --git a/saltoqd/storagemanager.cpp b/saltoqd/storagemanager.cpp
new file mode 100644
index 0000000..c2b9808
--- /dev/null
+++ b/saltoqd/storagemanager.cpp
@@ -0,0 +1,17 @@
+#include "storagemanager.h"
+
+StorageManager::StorageManager(ToqManager *toq) :
+ QObject(toq), _toq(toq)
+{
+ _toq->setEndpointListener(ToqConnection::StorageServiceEndpoint, this);
+}
+
+void StorageManager::handleMessage(const ToqConnection::Message &msg)
+{
+ Q_ASSERT(msg.destination == ToqConnection::StorageServiceEndpoint);
+ switch (msg.type) { // TODO
+ default:
+ qWarning() << "Unknown message" << msg.type;
+ break;
+ }
+}
diff --git a/saltoqd/storagemanager.h b/saltoqd/storagemanager.h
new file mode 100644
index 0000000..fa97a8d
--- /dev/null
+++ b/saltoqd/storagemanager.h
@@ -0,0 +1,29 @@
+#ifndef STORAGEMANAGER_H
+#define STORAGEMANAGER_H
+
+#include "toqmanager.h"
+
+class StorageManager : public QObject, public ToqManager::EndpointHandler
+{
+ Q_OBJECT
+public:
+ explicit StorageManager(ToqManager *toq);
+
+ void handleMessage(const ToqConnection::Message &msg) Q_DECL_OVERRIDE;
+
+ void updateStore(const QString &id, const QByteArray &data);
+
+private:
+ void handleGetStoreMessage(const ToqConnection::Message &msg);
+
+ struct Store {
+ QByteArray data;
+ quint32 checksum;
+ };
+
+private:
+ ToqManager *_toq;
+ QHash<QString, QByteArray> _stores;
+};
+
+#endif // STORAGEMANAGER_H
diff --git a/saltoqd/systemmanager.cpp b/saltoqd/systemmanager.cpp
new file mode 100644
index 0000000..4f74ea4
--- /dev/null
+++ b/saltoqd/systemmanager.cpp
@@ -0,0 +1,55 @@
+#include <QtCore/QDateTime>
+#include <QtCore/QTimeZone>
+#include "systemmanager.h"
+#include "voicecallmanager.h"
+
+SystemManager::SystemManager(ToqManager *toq) :
+ QObject(toq), _toq(toq)
+{
+ _toq->setEndpointListener(ToqConnection::SystemEndpoint, this);
+}
+
+void SystemManager::handleMessage(const ToqConnection::Message &msg)
+{
+ Q_ASSERT(msg.destination == ToqConnection::SystemEndpoint);
+ switch (msg.type) {
+ case 0:
+ handleGetTimeMessage(msg);
+ break;
+ case 7:
+ handleSilenceMessage(msg);
+ break;
+ default:
+ qWarning() << "Unknown system message" << msg.type;
+ break;
+ }
+}
+
+void SystemManager::handleGetTimeMessage(const ToqConnection::Message &msg)
+{
+ QJsonObject reply, time;
+ QDateTime dt = QDateTime::currentDateTime();
+ QTimeZone tz = dt.timeZone();
+
+ time.insert("epoch_time", qint64(dt.toTime_t()));
+ time.insert("time_zone", tz.standardTimeOffset(dt));
+ time.insert("dst", tz.isDaylightTime(dt) ? 1 : 0);
+
+ reply.insert("result", int(0));
+ reply.insert("description", QLatin1String("current time"));
+ reply.insert("time", time);
+
+ _toq->sendReply(msg, 0x4000, reply);
+}
+
+void SystemManager::handleSilenceMessage(const ToqConnection::Message &msg)
+{
+ QJsonObject reply;
+
+ reply.insert("result", int(0));
+ reply.insert("description", QLatin1String("Set to Silence Mode Request received"));
+
+ VoiceCallManager::setSilentMode(msg.payload.object()["silence_mode"].toInt());
+
+ _toq->sendReply(msg, 0x4007, reply);
+}
diff --git a/saltoqd/systemmanager.h b/saltoqd/systemmanager.h
new file mode 100644
index 0000000..9f7a005
--- /dev/null
+++ b/saltoqd/systemmanager.h
@@ -0,0 +1,22 @@
+#ifndef SYSTEMMANAGER_H
+#define SYSTEMMANAGER_H
+
+#include "toqmanager.h"
+
+class SystemManager : public QObject, public ToqManager::EndpointHandler
+{
+ Q_OBJECT
+public:
+ explicit SystemManager(ToqManager *toq);
+
+ void handleMessage(const ToqConnection::Message &msg) Q_DECL_OVERRIDE;
+
+private:
+ void handleGetTimeMessage(const ToqConnection::Message &msg);
+ void handleSilenceMessage(const ToqConnection::Message &msg);
+
+private:
+ ToqManager *_toq;
+};
+
+#endif // SYSTEMMANAGER_H
diff --git a/saltoqd/toqconnection.cpp b/saltoqd/toqconnection.cpp
new file mode 100644
index 0000000..c2e2349
--- /dev/null
+++ b/saltoqd/toqconnection.cpp
@@ -0,0 +1,187 @@
+#include <zlib.h>
+#include <QtDebug>
+#include <QtEndian>
+#include <QtCore/QMetaEnum>
+
+#include "toqconnection.h"
+
+static const int HEADER_LENGTH = 10;
+
+ToqConnection::ToqConnection(const QBluetoothAddress &address, QObject *parent) :
+ QObject(parent),
+ _address(address), _socket(0),
+ _reconnectTimer(new QTimer(this)),
+ _lastTransactionId(0)
+{
+ connect(_reconnectTimer, &QTimer::timeout,
+ this, &ToqConnection::tryConnect);
+
+ _reconnectTimer->setSingleShot(true);
+ _reconnectTimer->setInterval(1000);
+ _reconnectTimer->start();
+}
+
+const char * ToqConnection::nameOfEndpoint(Endpoint ep)
+{
+ int index = staticMetaObject.indexOfEnumerator("CoreEndpoints");
+ QMetaEnum epEnum = staticMetaObject.enumerator(index);
+ return epEnum.valueToKey(ep);
+}
+
+quint32 ToqConnection::checksum(const QByteArray &data)
+{
+ uLong crc = crc32(0L, Z_NULL, 0);
+ crc = crc32(crc, reinterpret_cast<const Bytef*>(data.constData()), data.size());
+ return crc;
+}
+
+quint32 ToqConnection::checksum(QIODevice *dev)
+{
+ uLong crc = crc32(0L, Z_NULL, 0);
+ char buffer[4 * 1024];
+ qint64 read;
+ while ((read = dev->read(buffer, sizeof(buffer))) > 0) {
+ crc = crc32(crc, reinterpret_cast<const Bytef*>(&buffer[0]), read);
+ }
+ return crc;
+}
+
+bool ToqConnection::isConnected() const
+{
+ return _socket && _socket->state() == QBluetoothSocket::ConnectedState;
+}
+
+quint16 ToqConnection::nextTransactionId()
+{
+ if (_lastTransactionId >= 0xFFFA) {
+ // The last transaction ids (as well as 0) seem to be reserved
+ // Avoid using them
+ _lastTransactionId = 0;
+ }
+
+ return ++_lastTransactionId;
+}
+
+void ToqConnection::sendMessage(const Message &msg)
+{
+ if (_socket) {
+ _socket->write(packMessage(msg));
+ } else {
+ qWarning() << "Discarding message because connection is broken";
+ }
+}
+
+ToqConnection::Message ToqConnection::unpackMessage(const QByteArray &data)
+{
+ Message msg;
+
+ Q_ASSERT(data.length() >= HEADER_LENGTH);
+ const uchar *header = reinterpret_cast<const uchar*>(data.constData());
+
+ quint16 message_length = qFromBigEndian<quint16>(&header[2]);
+ Q_ASSERT(data.length() == message_length + HEADER_LENGTH - 4);
+ msg.source = header[0];
+ msg.destination = header[1];
+ msg.transactionId = qFromBigEndian<quint16>(&header[4]);
+ msg.type = qFromBigEndian<quint32>(&header[6]);
+
+ if (!data.isEmpty()) {
+ QJsonParseError error;
+ msg.payload = QJsonDocument::fromJson(data.mid(HEADER_LENGTH), &error);
+ if (error.error) {
+ qWarning() << "Failure while parsing message JSON payload: " << error.errorString();
+ }
+ }
+
+ return msg;
+}
+
+QByteArray ToqConnection::packMessage(const Message &msg)
+{
+ QByteArray payload = msg.payload.toJson(QJsonDocument::Compact);
+ uchar header[HEADER_LENGTH];
+
+ header[0] = msg.source;
+ header[1] = msg.destination;
+ qToBigEndian<quint16>(payload.length() + 4, &header[2]);
+ qToBigEndian<quint16>(msg.transactionId, &header[4]);
+ qToBigEndian<quint32>(msg.type, &header[6]);
+
+ payload.prepend(reinterpret_cast<char*>(&header[0]), HEADER_LENGTH);
+
+ return payload;
+}
+
+void ToqConnection::tryConnect()
+{
+ Q_ASSERT(!_socket);
+
+ _socket = new QBluetoothSocket(QBluetoothServiceInfo::RfcommProtocol, this);
+ connect(_socket, &QBluetoothSocket::connected,
+ this, &ToqConnection::handleSocketConnected);
+ connect(_socket, &QBluetoothSocket::disconnected,
+ this, &ToqConnection::handleSocketDisconnected);
+ connect(_socket, (void (QBluetoothSocket::*)(QBluetoothSocket::SocketError))&QBluetoothSocket::error,
+ this, &ToqConnection::handleSocketError);
+ connect(_socket, &QBluetoothSocket::readyRead,
+ this, &ToqConnection::handleSocketData);
+
+ qDebug() << "Connecting to" << _address.toString();
+
+ _socket->connectToService(_address, 1, QIODevice::ReadWrite);
+}
+
+void ToqConnection::handleSocketConnected()
+{
+ qDebug() << "Connected";
+ Q_ASSERT(_socket);
+ _reconnectTimer->stop();
+ emit connected();
+}
+
+void ToqConnection::handleSocketDisconnected()
+{
+ qDebug() << "Disconnected";
+ Q_ASSERT(_socket);
+ _socket->deleteLater();
+ _socket = 0;
+ _reconnectTimer->start();
+ emit disconnected();
+}
+
+void ToqConnection::handleSocketError(QBluetoothSocket::SocketError error)
+{
+ qWarning() << error << _socket->errorString();
+}
+
+void ToqConnection::handleSocketData()
+{
+ // Keep attempting to read messages as long as at least a header is present
+ while (_socket->bytesAvailable() >= HEADER_LENGTH) {
+ // Take a look at the header, but do not remove it from the socket input buffer.
+ // We will only remove it once we're sure the entire packet is in the buffer.
+ uchar header[HEADER_LENGTH];
+ _socket->peek(reinterpret_cast<char*>(header), HEADER_LENGTH);
+
+ quint16 message_length = qFromBigEndian<quint16>(&header[2]);
+
+ // Sanity checks on the message_length
+ if (message_length == 0) {
+ qWarning() << "received empty message";
+ _socket->read(HEADER_LENGTH); // skip this header
+ continue; // check if there are additional headers.
+ }
+
+ // Now wait for the entire message
+ if (_socket->bytesAvailable() < HEADER_LENGTH + message_length - 4) {
+ qDebug() << "incomplete msg body in read buffer";
+ return; // try again once more data comes in
+ }
+
+ // We can now safely remove the message from the input buffer,
+ // as we know the entire message is in the input buffer.
+ QByteArray data = _socket->read(HEADER_LENGTH + message_length - 4);
+ emit messageReceived(unpackMessage(data));
+ }
+}
+
diff --git a/saltoqd/toqconnection.h b/saltoqd/toqconnection.h
new file mode 100644
index 0000000..47a3132
--- /dev/null
+++ b/saltoqd/toqconnection.h
@@ -0,0 +1,94 @@
+#ifndef TOQCONNECTION_H
+#define TOQCONNECTION_H
+
+#include <QtCore/QTimer>
+#include <QtBluetooth/QBluetoothSocket>
+#include <QtCore/QJsonDocument>
+#include <QtCore/QJsonObject>
+
+class ToqConnection : public QObject
+{
+ Q_OBJECT
+ Q_ENUMS(CoreEndpoints)
+ Q_PROPERTY(bool connected READ isConnected)
+
+public:
+ explicit ToqConnection(const QBluetoothAddress &address, QObject *parent = 0);
+
+ typedef quint8 Endpoint;
+
+ enum CoreEndpoints
+ {
+ VersionEndpoint = 0,
+ VoiceCallEndpoint = 1,
+ SMSEndpoint = 3,
+ SystemEndpoint = 5,
+ PopUpEndpoint = 7,
+ StorageServiceEndpoint = 9,
+ TFTPEndpoint = 15,
+ FMSEndpoint = 17,
+ EPCommunicationEndpoint = 19,
+ MusicEndpoint = 24,
+ AppMessagingEndpoint = 26,
+ SpeechEndpoint = 28,
+ ActivityMonitoringEndpoint = 30,
+ FTSEndpoint = 32,
+ AppLoggingEndpoint = 34
+ };
+
+ struct Message
+ {
+ Message();
+ Message(Endpoint source, Endpoint destination, quint16 transactionId, quint32 type, QJsonDocument payload);
+
+ Endpoint source;
+ Endpoint destination;
+ quint16 transactionId;
+ quint32 type;
+ QJsonDocument payload;
+ };
+
+ static const char * nameOfEndpoint(Endpoint ep);
+
+ static quint32 checksum(const QByteArray &data);
+ static quint32 checksum(QIODevice *dev);
+
+ bool isConnected() const;
+ quint16 nextTransactionId();
+
+public slots:
+ void sendMessage(const Message &msg);
+
+signals:
+ void connected();
+ void disconnected();
+ void messageReceived(const Message &msg);
+
+private:
+ Message unpackMessage(const QByteArray &data);
+ QByteArray packMessage(const Message &msg);
+
+private slots:
+ void tryConnect();
+ void handleSocketConnected();
+ void handleSocketDisconnected();
+ void handleSocketError(QBluetoothSocket::SocketError error);
+ void handleSocketData();
+
+private:
+ QBluetoothAddress _address;
+ QBluetoothSocket *_socket;
+ QTimer *_reconnectTimer;
+ quint16 _lastTransactionId;
+};
+
+inline ToqConnection::Message::Message()
+{
+}
+
+inline ToqConnection::Message::Message(Endpoint source, Endpoint destination, quint16 transactionId, quint32 type, QJsonDocument payload)
+ : source(source), destination(destination), transactionId(transactionId), type(type), payload(payload)
+{
+}
+
+#endif // TOQCONNECTION_H
diff --git a/saltoqd/toqmanager.cpp b/saltoqd/toqmanager.cpp
new file mode 100644
index 0000000..2e0d967
--- /dev/null
+++ b/saltoqd/toqmanager.cpp
@@ -0,0 +1,73 @@
+#include "toqmanager.h"
+
+#include "versionmanager.h"
+#include "systemmanager.h"
+#include "storagemanager.h"
+#include "musicmanager.h"
+#include "commmanager.h"
+
+ToqManager::ToqManager(const QBluetoothAddress &address, QObject *parent) :
+ QObject(parent),
+ _conn(new ToqConnection(address, this)),
+ _versionManager(new VersionManager(this)),
+ _systemManager(new SystemManager(this)),
+ _storageManager(new StorageManager(this)),
+ _musicManager(new MusicManager(this)),
+ _commManager(new CommManager(_storageManager, this))
+{
+ connect(_conn, &ToqConnection::messageReceived,
+ this, &ToqManager::handleToqMessage);
+}
+
+void ToqManager::setEndpointListener(ToqConnection::Endpoint ep, EndpointHandler *handler)
+{
+ Q_ASSERT(!_handlers.contains(ep));
+ _handlers.insert(ep, handler);
+}
+
+void ToqManager::sendMessage(const ToqConnection::Message &msg)
+{
+ if (1) {
+ QString json = QString::fromUtf8(msg.payload.toJson(QJsonDocument::Compact));
+ qDebug() << "Sending message to" << ToqConnection::nameOfEndpoint(msg.destination) << "from" << ToqConnection::nameOfEndpoint(msg.destination) << "type" << msg.type << json;
+ }
+ _conn->sendMessage(msg);
+}
+
+void ToqManager::sendMessage(ToqConnection::Endpoint source, ToqConnection::Endpoint destination, quint16 transactionId, quint32 type, const QJsonObject &payload)
+{
+ QJsonDocument doc(payload);
+ ToqConnection::Message msg(source, destination, transactionId, type, doc);
+ _conn->sendMessage(msg);
+}
+
+quint16 ToqManager::sendMessage(ToqConnection::Endpoint source, ToqConnection::Endpoint destination, quint32 type, const QJsonObject &payload)
+{
+ QJsonDocument doc(payload);
+ quint16 transactionId = _conn->nextTransactionId();
+ ToqConnection::Message msg(source, destination, transactionId, type, doc);
+ _conn->sendMessage(msg);
+ return transactionId;
+}
+
+void ToqManager::sendReply(const ToqConnection::Message &msg, quint32 type, const QJsonObject &payload)
+{
+ ToqConnection::Message reply(msg.destination, msg.source, msg.transactionId, type, QJsonDocument(payload));
+ _conn->sendMessage(reply);
+}
+
+void ToqManager::handleToqMessage(const ToqConnection::Message &msg)
+{
+ EndpointHandler *handler = _handlers.value(msg.destination, 0);
+
+ if (1) {
+ QString json = QString::fromUtf8(msg.payload.toJson(QJsonDocument::Compact));
+ qDebug() << "Received message to" << ToqConnection::nameOfEndpoint(msg.destination) << "from" << ToqConnection::nameOfEndpoint(msg.destination) << "type" << msg.type << json;
+ }
+
+ if (handler) {
+ handler->handleMessage(msg);
+ } else {
+ qWarning() << "No registered handler for endpoint" << ToqConnection::nameOfEndpoint(msg.destination);
+ }
+}
diff --git a/saltoqd/toqmanager.h b/saltoqd/toqmanager.h
new file mode 100644
index 0000000..0edd6ec
--- /dev/null
+++ b/saltoqd/toqmanager.h
@@ -0,0 +1,54 @@
+#ifndef TOQMANAGER_H
+#define TOQMANAGER_H
+
+#include <functional>
+#include <QtCore/QObject>
+
+#include "toqconnection.h"
+
+class VersionManager;
+class SystemManager;
+class StorageManager;
+class MusicManager;
+class CommManager;
+
+class ToqManager : public QObject
+{
+ Q_OBJECT
+public:
+ explicit ToqManager(const QBluetoothAddress &address, QObject *parent = 0);
+
+ struct EndpointHandler {
+ virtual void handleMessage(const ToqConnection::Message &msg) = 0;
+ };
+
+ void setEndpointListener(ToqConnection::Endpoint ep, EndpointHandler *handler);
+
+ bool isConnected() const;
+ void sendMessage(const ToqConnection::Message &msg);
+ void sendMessage(ToqConnection::Endpoint source, ToqConnection::Endpoint destination,
+ quint16 transactionId, quint32 type, const QJsonObject &payload);
+ quint16 sendMessage(ToqConnection::Endpoint source, ToqConnection::Endpoint destination,
+ quint32 type, const QJsonObject &payload);
+ void sendReply(const ToqConnection::Message &msg, quint32 type, const QJsonObject &payload);
+
+private slots:
+ void handleToqMessage(const ToqConnection::Message &msg);
+
+private:
+ ToqConnection *_conn;
+ QHash<ToqConnection::Endpoint, EndpointHandler*> _handlers;
+
+ VersionManager *_versionManager;
+ SystemManager *_systemManager;
+ StorageManager *_storageManager;
+ MusicManager *_musicManager;
+ CommManager *_commManager;
+};
+
+inline bool ToqManager::isConnected() const
+{
+ return _conn->isConnected();
+}
+
+#endif // TOQMANAGER_H
diff --git a/saltoqd/versionmanager.cpp b/saltoqd/versionmanager.cpp
new file mode 100644
index 0000000..8c88992
--- /dev/null
+++ b/saltoqd/versionmanager.cpp
@@ -0,0 +1,32 @@
+#include "versionmanager.h"
+
+VersionManager::VersionManager(ToqManager *toq) :
+ QObject(toq), _toq(toq)
+{
+ _toq->setEndpointListener(ToqConnection::VersionEndpoint, this);
+}
+
+void VersionManager::handleMessage(const ToqConnection::Message &msg)
+{
+ Q_ASSERT(msg.destination == ToqConnection::VersionEndpoint);
+ switch (msg.type) {
+ case 0:
+ handleVersionMessage(msg);
+ break;
+ default:
+ qWarning() << "Unknown version message" << msg.type;
+ break;
+ }
+}
+
+void VersionManager::handleVersionMessage(const ToqConnection::Message &msg)
+{
+ QJsonObject root = msg.payload.object();
+ qDebug() << "Remote AlohaVersion: " << root["AlohaVersion"].toString();
+
+ QJsonObject reply;
+ reply.insert("PhoneType", QJsonValue(QLatin1String("Android")));
+ reply.insert("SoftwareRelease", QJsonValue(QLatin1String("4.4.2")));
+
+ _toq->sendReply(msg, 1, reply);
+}
diff --git a/saltoqd/versionmanager.h b/saltoqd/versionmanager.h
new file mode 100644
index 0000000..11c0173
--- /dev/null
+++ b/saltoqd/versionmanager.h
@@ -0,0 +1,21 @@
+#ifndef VERSIONMANAGER_H
+#define VERSIONMANAGER_H
+
+#include "toqmanager.h"
+
+class VersionManager : public QObject, public ToqManager::EndpointHandler
+{
+ Q_OBJECT
+public:
+ explicit VersionManager(ToqManager *toq);
+
+ void handleMessage(const ToqConnection::Message &msg) Q_DECL_OVERRIDE;
+
+private:
+ void handleVersionMessage(const ToqConnection::Message &msg);
+
+private:
+ ToqManager *_toq;
+};
+
+#endif // VERSIONMANAGER_H
diff --git a/saltoqd/voicecallmanager.cpp b/saltoqd/voicecallmanager.cpp
new file mode 100644
index 0000000..3ef9223
--- /dev/null
+++ b/saltoqd/voicecallmanager.cpp
@@ -0,0 +1,73 @@
+#include <QtDBus/QDBusConnection>
+#include "voicecallmanager.h"
+#include "voicecallmanager_interface.h"
+
+static OrgNemomobileVoicecallVoiceCallManagerInterface *vcm = NULL;
+
+VoiceCallManager::VoiceCallManager(ToqManager *toq) :
+ QObject(toq), _toq(toq)
+{
+ if (!vcm) {
+ vcm = new OrgNemomobileVoicecallVoiceCallManagerInterface("org.nemomobile.voicecall",
+ "/",
+ QDBusConnection::sessionBus());
+ }
+
+ _toq->setEndpointListener(ToqConnection::VoiceCallEndpoint, this);
+}
+
+void VoiceCallManager::handleMessage(const ToqConnection::Message &msg)
+{
+ Q_ASSERT(msg.destination == ToqConnection::VoiceCallEndpoint);
+ switch (msg.type) {
+ case 15:
+ handleGetPhoneStatusMessage(msg);
+ break;
+ default:
+ qWarning() << "Unknown message" << msg.type;
+ break;
+ }
+}
+
+void VoiceCallManager::setSilentMode(bool silent)
+{
+ setProfile(silent ? "silent" : "ambience");
+}
+
+void VoiceCallManager::handleGetPhoneStatusMessage(const ToqConnection::Message &msg)
+{
+ 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);
+
+ _toq->sendReply(msg, 0x400F, obj);
+}
+
+QString VoiceCallManager::getCurrentProfile()
+{
+ QDBusConnection bus = QDBusConnection::sessionBus();
+ QDBusReply<QString> resp = bus.call(QDBusMessage::createMethodCall("com.nokia.profiled",
+ "/com/nokia/profiled",
+ "com.nokia.profiled",
+ "get_profile"));
+ if (resp.isValid()) {
+ return resp.value();
+ } else {
+ qWarning() << resp.error().message();
+ return QString();
+ }
+}
+
+void VoiceCallManager::setProfile(const QString &name)
+{
+ QDBusConnection bus = QDBusConnection::sessionBus();
+ QDBusReply<bool> resp = bus.call(QDBusMessage::createMethodCall("com.nokia.profiled",
+ "/com/nokia/profiled",
+ "com.nokia.profiled",
+ "set_profile") << name);
+ if (!resp.isValid()) {
+ qWarning() << resp.error().message();
+ }
+}
diff --git a/saltoqd/voicecallmanager.h b/saltoqd/voicecallmanager.h
new file mode 100644
index 0000000..eb9e38e
--- /dev/null
+++ b/saltoqd/voicecallmanager.h
@@ -0,0 +1,26 @@
+#ifndef VOICECALLMANAGER_H
+#define VOICECALLMANAGER_H
+
+#include "toqmanager.h"
+
+class VoiceCallManager : public QObject, public ToqManager::EndpointHandler
+{
+ Q_OBJECT
+public:
+ explicit VoiceCallManager(ToqManager *toq);
+
+ void handleMessage(const ToqConnection::Message &msg) Q_DECL_OVERRIDE;
+
+ static void setSilentMode(bool silent);
+
+private:
+ void handleGetPhoneStatusMessage(const ToqConnection::Message &msg);
+
+ static QString getCurrentProfile();
+ static void setProfile(const QString &name);
+
+private:
+ ToqManager *_toq;
+};
+
+#endif // VOICECALLMANAGER_H