summaryrefslogtreecommitdiff
path: root/rockworkd/platformintegration/ubuntu
diff options
context:
space:
mode:
authorAndrew Branson <andrew.branson@cern.ch>2016-02-11 23:55:16 +0100
committerAndrew Branson <andrew.branson@cern.ch>2016-02-11 23:55:16 +0100
commit29aaea2d80a9eb1715b6cddfac2d2aacf76358bd (patch)
tree012795b6bec16c72f38d33cff46324c9a0225868 /rockworkd/platformintegration/ubuntu
launchpad ~mzanetti/rockwork/trunk r87
Diffstat (limited to 'rockworkd/platformintegration/ubuntu')
-rw-r--r--rockworkd/platformintegration/ubuntu/callchannelobserver.cpp165
-rw-r--r--rockworkd/platformintegration/ubuntu/callchannelobserver.h74
-rw-r--r--rockworkd/platformintegration/ubuntu/organizeradapter.cpp74
-rw-r--r--rockworkd/platformintegration/ubuntu/organizeradapter.h33
-rw-r--r--rockworkd/platformintegration/ubuntu/syncmonitorclient.cpp100
-rw-r--r--rockworkd/platformintegration/ubuntu/syncmonitorclient.h51
-rw-r--r--rockworkd/platformintegration/ubuntu/ubuntuplatform.cpp232
-rw-r--r--rockworkd/platformintegration/ubuntu/ubuntuplatform.h62
8 files changed, 791 insertions, 0 deletions
diff --git a/rockworkd/platformintegration/ubuntu/callchannelobserver.cpp b/rockworkd/platformintegration/ubuntu/callchannelobserver.cpp
new file mode 100644
index 0000000..e3d852c
--- /dev/null
+++ b/rockworkd/platformintegration/ubuntu/callchannelobserver.cpp
@@ -0,0 +1,165 @@
+#include "callchannelobserver.h"
+
+#include <TelepathyQt/Contact>
+#include <TelepathyQt/PendingContactInfo>
+
+#include <QContactFetchRequest>
+#include <QContactPhoneNumber>
+#include <QContactFilter>
+#include <QContactDetail>
+#include <QContactDisplayLabel>
+
+QTCONTACTS_USE_NAMESPACE
+
+TelepathyMonitor::TelepathyMonitor(QObject *parent):
+ QObject(parent)
+{
+ Tp::registerTypes();
+ QTimer::singleShot(0, this, &TelepathyMonitor::accountManagerSetup);
+ m_contactManager = new QContactManager("galera");
+ m_contactManager->setParent(this);
+}
+
+void TelepathyMonitor::hangupCall(uint cookie)
+{
+ if (m_currentCalls.contains(cookie)) {
+ m_currentCalls.value(cookie)->hangup();
+ }
+}
+
+void TelepathyMonitor::accountManagerSetup()
+{
+ m_accountManager = Tp::AccountManager::create(Tp::AccountFactory::create(QDBusConnection::sessionBus(),
+ Tp::Account::FeatureCore),
+ Tp::ConnectionFactory::create(QDBusConnection::sessionBus(),
+ Tp::Connection::FeatureCore));
+ connect(m_accountManager->becomeReady(),
+ SIGNAL(finished(Tp::PendingOperation*)),
+ SLOT(accountManagerReady(Tp::PendingOperation*)));
+}
+
+void TelepathyMonitor::accountManagerReady(Tp::PendingOperation* operation)
+{
+ if (operation->isError()) {
+ qDebug() << "TelepathyMonitor: accountManager init error.";
+ QTimer::singleShot(1000, this, &TelepathyMonitor::accountManagerSetup); // again
+ return;
+ }
+ qDebug() << "Telepathy account manager ready";
+
+ foreach (const Tp::AccountPtr& account, m_accountManager->allAccounts()) {
+ connect(account->becomeReady(Tp::Account::FeatureCapabilities),
+ SIGNAL(finished(Tp::PendingOperation*)),
+ SLOT(accountReady(Tp::PendingOperation*)));
+ }
+
+ connect(m_accountManager.data(), SIGNAL(newAccount(Tp::AccountPtr)), SLOT(newAccount(Tp::AccountPtr)));
+}
+
+void TelepathyMonitor::newAccount(const Tp::AccountPtr& account)
+{
+ connect(account->becomeReady(Tp::Account::FeatureCapabilities),
+ SIGNAL(finished(Tp::PendingOperation*)),
+ SLOT(accountReady(Tp::PendingOperation*)));
+}
+
+void TelepathyMonitor::accountReady(Tp::PendingOperation* operation)
+{
+ if (operation->isError()) {
+ qDebug() << "TelepathyAccount: Operation failed (accountReady)";
+ return;
+ }
+
+ Tp::PendingReady* pendingReady = qobject_cast<Tp::PendingReady*>(operation);
+ if (pendingReady == 0) {
+ qDebug() << "Rejecting account because could not understand ready status";
+ return;
+ }
+ checkAndAddAccount(Tp::AccountPtr::qObjectCast(pendingReady->proxy()));
+}
+
+void TelepathyMonitor::onCallStarted(Tp::CallChannelPtr callChannel)
+{
+ // Haven't figured how to send outgoing calls to pebble yet... discard it
+ if (callChannel->initiatorContact()->id().isEmpty()) {
+ qWarning() << "ignoring phone call. looks like it's an outgoing one";
+ return;
+ }
+
+ m_cookie++;
+ m_currentCalls.insert(m_cookie, callChannel.data());
+ m_currentCallStates.insert(m_cookie, Tp::CallStateInitialising);
+
+ callChannel->becomeReady(Tp::CallChannel::FeatureCallState);
+
+ connect(callChannel.data(), &Tp::CallChannel::callStateChanged, this, &TelepathyMonitor::callStateChanged);
+
+ QString number = callChannel->initiatorContact()->id();
+ qDebug() << "call started" << number;
+
+ // try to match the contact info
+ QContactFetchRequest *request = new QContactFetchRequest(this);
+ request->setFilter(QContactPhoneNumber::match(number));
+
+ // lambda function to update the notification
+ QObject::connect(request, &QContactAbstractRequest::stateChanged, [this, request, number](QContactAbstractRequest::State state) {
+ qDebug() << "request returned";
+ if (!request || state != QContactAbstractRequest::FinishedState) {
+ qDebug() << "error fetching contact" << state;
+ return;
+ }
+
+ QContact contact;
+
+ // create the snap decision only after the contact match finishes
+ if (request->contacts().size() > 0) {
+ // use the first match
+ contact = request->contacts().at(0);
+
+ qDebug() << "have contact" << contact.detail<QContactDisplayLabel>().label();
+ emit this->incomingCall(m_cookie, number, contact.detail<QContactDisplayLabel>().label());
+ } else {
+ qDebug() << "unknown contact" << number;
+ emit this->incomingCall(m_cookie, number, QString());
+ }
+ });
+
+ request->setManager(m_contactManager);
+ request->start();
+}
+
+void TelepathyMonitor::callStateChanged(Tp::CallState state)
+{
+ qDebug() << "call state changed1";
+ Tp::CallChannel *channel = qobject_cast<Tp::CallChannel*>(sender());
+ uint cookie = m_currentCalls.key(channel);
+
+ qDebug() << "call state changed2" << state << "cookie:" << cookie;
+
+ switch (state) {
+ case Tp::CallStateActive:
+ emit callStarted(cookie);
+ m_currentCallStates[cookie] = Tp::CallStateActive;
+ break;
+ case Tp::CallStateEnded: {
+ Tp::CallState oldState = m_currentCallStates.value(cookie);
+ emit callEnded(cookie, oldState != Tp::CallStateActive);
+ m_currentCalls.take(cookie);
+ m_currentCallStates.take(cookie);
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+void TelepathyMonitor::checkAndAddAccount(const Tp::AccountPtr& account)
+{
+ Tp::ConnectionCapabilities caps = account->capabilities();
+ // TODO: Later on we will need to filter for the right capabilities, and also allow dynamic account detection
+ // Don't check caps for now as a workaround for https://bugs.launchpad.net/ubuntu/+source/media-hub/+bug/1409125
+ // at least until we are able to find out the root cause of it (check rev 107 for the caps check)
+ auto tcm = new TelepathyCallMonitor(account);
+ connect(tcm, &TelepathyCallMonitor::callStarted, this, &TelepathyMonitor::onCallStarted);
+ m_callMonitors.append(tcm);
+}
diff --git a/rockworkd/platformintegration/ubuntu/callchannelobserver.h b/rockworkd/platformintegration/ubuntu/callchannelobserver.h
new file mode 100644
index 0000000..cc2b7aa
--- /dev/null
+++ b/rockworkd/platformintegration/ubuntu/callchannelobserver.h
@@ -0,0 +1,74 @@
+#ifndef CALLCHANNELOBSERVER_H
+#define CALLCHANNELOBSERVER_H
+
+#include <TelepathyQt/AccountManager>
+#include <TelepathyQt/SimpleCallObserver>
+#include <TelepathyQt/PendingOperation>
+#include <TelepathyQt/PendingReady>
+#include <TelepathyQt/PendingAccount>
+#include <TelepathyQt/CallChannel>
+
+#include <QContactManager>
+
+QTCONTACTS_USE_NAMESPACE
+
+class TelepathyCallMonitor : public QObject
+{
+ Q_OBJECT
+public:
+ TelepathyCallMonitor(const Tp::AccountPtr& account):
+ mAccount(account),
+ mCallObserver(Tp::SimpleCallObserver::create(mAccount)) {
+ connect(mCallObserver.data(), SIGNAL(callStarted(Tp::CallChannelPtr)), SIGNAL(callStarted(Tp::CallChannelPtr)));
+// connect(mCallObserver.data(), SIGNAL(callEnded(Tp::CallChannelPtr,QString,QString)), SIGNAL(callEnded()));
+// connect(mCallObserver.data(), SIGNAL(streamedMediaCallStarted(Tp::StreamedMediaChannelPtr)), SIGNAL(offHook()));
+// connect(mCallObserver.data(), SIGNAL(streamedMediaCallEnded(Tp::StreamedMediaChannelPtr,QString,QString)), SIGNAL(onHook()));
+ }
+
+signals:
+ void callStarted(Tp::CallChannelPtr callChannel);
+// void callEnded();
+
+private:
+ Tp::AccountPtr mAccount;
+ Tp::SimpleCallObserverPtr mCallObserver;
+};
+
+class TelepathyMonitor: public QObject
+{
+ Q_OBJECT
+public:
+ TelepathyMonitor(QObject *parent = 0);
+
+ void hangupCall(uint cookie);
+
+private slots:
+ void accountManagerSetup();
+ void accountManagerReady(Tp::PendingOperation* operation);
+
+ void newAccount(const Tp::AccountPtr& account);
+ void accountReady(Tp::PendingOperation* operation);
+
+ void onCallStarted(Tp::CallChannelPtr callChannel);
+ void callStateChanged(Tp::CallState state);
+
+signals:
+ void incomingCall(uint cookie, const QString &number, const QString &name);
+ void callStarted(uint cookie);
+ void callEnded(uint cookie, bool missed);
+
+private:
+ void checkAndAddAccount(const Tp::AccountPtr& account);
+
+private:
+ Tp::AccountManagerPtr m_accountManager;
+ QList<TelepathyCallMonitor*> m_callMonitors;
+ QContactManager *m_contactManager;
+
+ QHash<uint, Tp::CallChannel*> m_currentCalls;
+ QHash<uint, Tp::CallState> m_currentCallStates;
+
+ uint m_cookie = 0;
+};
+
+#endif // CALLCHANNELOBSERVER_H
diff --git a/rockworkd/platformintegration/ubuntu/organizeradapter.cpp b/rockworkd/platformintegration/ubuntu/organizeradapter.cpp
new file mode 100644
index 0000000..853403a
--- /dev/null
+++ b/rockworkd/platformintegration/ubuntu/organizeradapter.cpp
@@ -0,0 +1,74 @@
+#include "organizeradapter.h"
+
+#include <QOrganizerItemFetchRequest>
+#include <QDebug>
+#include <QOrganizerEventOccurrence>
+#include <QOrganizerItemDetail>
+
+QTORGANIZER_USE_NAMESPACE
+
+#define MANAGER "eds"
+#define MANAGER_FALLBACK "memory"
+
+OrganizerAdapter::OrganizerAdapter(QObject *parent) : QObject(parent)
+{
+ QString envManager(qgetenv("ALARM_BACKEND"));
+ if (envManager.isEmpty())
+ envManager = MANAGER;
+ if (!QOrganizerManager::availableManagers().contains(envManager)) {
+ envManager = MANAGER_FALLBACK;
+ }
+ m_manager = new QOrganizerManager(envManager);
+ m_manager->setParent(this);
+ connect(m_manager, &QOrganizerManager::dataChanged, this, &OrganizerAdapter::refresh);
+}
+
+void OrganizerAdapter::refresh()
+{
+ QList<CalendarEvent> items;
+ foreach (const QOrganizerItem &item, m_manager->items()) {
+ QOrganizerEvent organizerEvent(item);
+ if (organizerEvent.displayLabel().isEmpty()) {
+ continue;
+ }
+ CalendarEvent event;
+ event.setId(organizerEvent.id().toString());
+ event.setTitle(organizerEvent.displayLabel());
+ event.setDescription(organizerEvent.description());
+ event.setStartTime(organizerEvent.startDateTime());
+ event.setEndTime(organizerEvent.endDateTime());
+ event.setLocation(organizerEvent.location());
+ event.setComment(organizerEvent.comments().join(";"));
+ QStringList attendees;
+ foreach (const QOrganizerItemDetail &attendeeDetail, organizerEvent.details(QOrganizerItemDetail::TypeEventAttendee)) {
+ attendees.append(attendeeDetail.value(QOrganizerItemDetail::TypeEventAttendee + 1).toString());
+ }
+ event.setGuests(attendees);
+ event.setRecurring(organizerEvent.recurrenceRules().count() > 0);
+
+ items.append(event);
+
+ quint64 startTimestamp = QDateTime::currentMSecsSinceEpoch();
+ startTimestamp -= (1000 * 60 * 60 * 24 * 7);
+
+ foreach (const QOrganizerItem &occurranceItem, m_manager->itemOccurrences(item, QDateTime::fromMSecsSinceEpoch(startTimestamp), QDateTime::currentDateTime().addDays(7))) {
+ QOrganizerEventOccurrence organizerOccurrance(occurranceItem);
+ event.generateNewUuid();
+ event.setId(organizerOccurrance.id().toString());
+ event.setStartTime(organizerOccurrance.startDateTime());
+ event.setEndTime(organizerOccurrance.endDateTime());
+ items.append(event);
+ }
+ }
+
+ if (m_items != items) {
+ m_items = items;
+ emit itemsChanged(m_items);
+ }
+
+}
+
+QList<CalendarEvent> OrganizerAdapter::items() const
+{
+ return m_items;
+}
diff --git a/rockworkd/platformintegration/ubuntu/organizeradapter.h b/rockworkd/platformintegration/ubuntu/organizeradapter.h
new file mode 100644
index 0000000..2ce8e4d
--- /dev/null
+++ b/rockworkd/platformintegration/ubuntu/organizeradapter.h
@@ -0,0 +1,33 @@
+#ifndef ORGANIZERADAPTER_H
+#define ORGANIZERADAPTER_H
+
+#include "libpebble/calendarevent.h"
+
+#include <QObject>
+
+#include <QOrganizerManager>
+#include <QOrganizerAbstractRequest>
+#include <QOrganizerEvent>
+
+QTORGANIZER_USE_NAMESPACE
+
+class OrganizerAdapter : public QObject
+{
+ Q_OBJECT
+public:
+ explicit OrganizerAdapter(QObject *parent = 0);
+
+ QList<CalendarEvent> items() const;
+
+public slots:
+ void refresh();
+
+signals:
+ void itemsChanged(const QList<CalendarEvent> &items);
+
+private:
+ QOrganizerManager *m_manager;
+ QList<CalendarEvent> m_items;
+};
+
+#endif // ORGANIZERADAPTER_H
diff --git a/rockworkd/platformintegration/ubuntu/syncmonitorclient.cpp b/rockworkd/platformintegration/ubuntu/syncmonitorclient.cpp
new file mode 100644
index 0000000..b43509e
--- /dev/null
+++ b/rockworkd/platformintegration/ubuntu/syncmonitorclient.cpp
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2014 Canonical Ltd.
+ *
+ * This file is part of sync-monitor.
+ *
+ * sync-monitor is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 3.
+ *
+ * contact-service-app is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <QDebug>
+#include <QTimer>
+
+#include "syncmonitorclient.h"
+
+#define SYNCMONITOR_DBUS_SERVICE_NAME "com.canonical.SyncMonitor"
+#define SYNCMONITOR_DBUS_OBJECT_PATH "/com/canonical/SyncMonitor"
+#define SYNCMONITOR_DBUS_INTERFACE "com.canonical.SyncMonitor"
+
+
+SyncMonitorClient::SyncMonitorClient(QObject *parent)
+ : QObject(parent),
+ m_iface(0)
+{
+ m_iface = new QDBusInterface(SYNCMONITOR_DBUS_SERVICE_NAME,
+ SYNCMONITOR_DBUS_OBJECT_PATH,
+ SYNCMONITOR_DBUS_INTERFACE);
+ if (m_iface->lastError().isValid()) {
+ qWarning() << "Fail to connect with sync monitor:" << m_iface->lastError();
+ return;
+ }
+
+ connect(m_iface, SIGNAL(stateChanged()), SIGNAL(stateChanged()));
+ connect(m_iface, SIGNAL(enabledServicesChanged()), SIGNAL(enabledServicesChanged()));
+ m_iface->call("attach");
+}
+
+SyncMonitorClient::~SyncMonitorClient()
+{
+ if (m_iface) {
+ m_iface->call("detach");
+ delete m_iface;
+ m_iface = 0;
+ }
+}
+
+QString SyncMonitorClient::state() const
+{
+ if (m_iface) {
+ return m_iface->property("state").toString();
+ } else {
+ return "";
+ }
+}
+
+QStringList SyncMonitorClient::enabledServices() const
+{
+ if (m_iface) {
+ return m_iface->property("enabledServices").toStringList();
+ } else {
+ return QStringList();
+ }
+}
+
+/*!
+ Start a new sync for specified services
+*/
+void SyncMonitorClient::sync(const QStringList &services)
+{
+ if (m_iface) {
+ qDebug() << "starting sync!";
+ m_iface->call("sync", services);
+ }
+}
+
+/*!
+ Cancel current sync for specified services
+*/
+void SyncMonitorClient::cancel(const QStringList &services)
+{
+ if (m_iface) {
+ m_iface->call("cancel", services);
+ }
+}
+
+/*!
+ Chek if a specific service is enabled or not
+*/
+bool SyncMonitorClient::serviceIsEnabled(const QString &service)
+{
+ return enabledServices().contains(service);
+}
diff --git a/rockworkd/platformintegration/ubuntu/syncmonitorclient.h b/rockworkd/platformintegration/ubuntu/syncmonitorclient.h
new file mode 100644
index 0000000..1587ba5
--- /dev/null
+++ b/rockworkd/platformintegration/ubuntu/syncmonitorclient.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2014 Canonical Ltd.
+ *
+ * This file is part of sync-monitor.
+ *
+ * sync-monitor is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 3.
+ *
+ * contact-service-app is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef SYNCMONITOR_QML_H
+#define SYNCMONITOR_QML_H
+
+#include <QObject>
+#include <QDBusInterface>
+
+class SyncMonitorClient : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QString state READ state NOTIFY stateChanged)
+ Q_PROPERTY(QStringList enabledServices READ enabledServices NOTIFY enabledServicesChanged)
+
+public:
+ SyncMonitorClient(QObject *parent = 0);
+ ~SyncMonitorClient();
+
+ QString state() const;
+ QStringList enabledServices() const;
+
+Q_SIGNALS:
+ void stateChanged();
+ void enabledServicesChanged();
+
+public Q_SLOTS:
+ void sync(const QStringList &services);
+ void cancel(const QStringList &services);
+ bool serviceIsEnabled(const QString &service);
+
+private:
+ QDBusInterface *m_iface;
+};
+
+#endif
diff --git a/rockworkd/platformintegration/ubuntu/ubuntuplatform.cpp b/rockworkd/platformintegration/ubuntu/ubuntuplatform.cpp
new file mode 100644
index 0000000..7c060b1
--- /dev/null
+++ b/rockworkd/platformintegration/ubuntu/ubuntuplatform.cpp
@@ -0,0 +1,232 @@
+#include "ubuntuplatform.h"
+
+#include "callchannelobserver.h"
+#include "organizeradapter.h"
+#include "syncmonitorclient.h"
+
+#include <QDBusConnection>
+#include <QDBusConnectionInterface>
+#include <QDebug>
+
+// qmenumodel
+#include "dbus-enums.h"
+#include "liburl-dispatcher-1/url-dispatcher.h"
+
+UbuntuPlatform::UbuntuPlatform(QObject *parent):
+ PlatformInterface(parent),
+ m_volumeActionGroup()
+{
+ // Notifications
+ QDBusConnection::sessionBus().registerObject("/org/freedesktop/Notifications", this, QDBusConnection::ExportAllSlots);
+ m_iface = new QDBusInterface("org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus");
+ m_iface->call("AddMatch", "interface='org.freedesktop.Notifications',member='Notify',type='method_call',eavesdrop='true'");
+ m_iface->call("AddMatch", "interface='org.freedesktop.Notifications',member='CloseNotification',type='method_call',eavesdrop='true'");
+
+ // Music
+ setupMusicService();
+ m_volumeActionGroup.setBusType(DBusEnums::SessionBus);
+ m_volumeActionGroup.setBusName("com.canonical.indicator.sound");
+ m_volumeActionGroup.setObjectPath("/com/canonical/indicator/sound");
+ m_volumeActionGroup.QDBusObject::connect();
+ connect(&m_volumeActionGroup, &QDBusActionGroup::statusChanged, [this] {
+ if (m_volumeActionGroup.status() == DBusEnums::Connected) {
+ m_volumeAction = m_volumeActionGroup.action("volume");
+ }
+ });
+
+ // Calls
+ m_telepathyMonitor = new TelepathyMonitor(this);
+ connect(m_telepathyMonitor, &TelepathyMonitor::incomingCall, this, &UbuntuPlatform::incomingCall);
+ connect(m_telepathyMonitor, &TelepathyMonitor::callStarted, this, &UbuntuPlatform::callStarted);
+ connect(m_telepathyMonitor, &TelepathyMonitor::callEnded, this, &UbuntuPlatform::callEnded);
+
+ // Organizer
+ m_organizerAdapter = new OrganizerAdapter(this);
+ m_organizerAdapter->refresh();
+ connect(m_organizerAdapter, &OrganizerAdapter::itemsChanged, this, &UbuntuPlatform::organizerItemsChanged);
+ m_syncMonitorClient = new SyncMonitorClient(this);
+ connect(m_syncMonitorClient, &SyncMonitorClient::stateChanged, [this]() { if (m_syncMonitorClient->state() == "idle") m_organizerAdapter->refresh();});
+ m_syncTimer.start(1000 * 60 * 60);
+ connect(&m_syncTimer, &QTimer::timeout, [this]() { m_syncMonitorClient->sync({"calendar"});});
+ m_syncMonitorClient->sync({"calendar"});
+}
+
+QDBusInterface *UbuntuPlatform::interface() const
+{
+ return m_iface;
+}
+
+uint UbuntuPlatform::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)
+{
+ Q_UNUSED(replaces_id)
+ // Lets directly suppress volume change notifications, network password entries and phone call snap decisions here
+ QStringList hiddenNotifications = {"indicator-sound", "indicator-network"};
+ if (!hiddenNotifications.contains(app_name)) {
+ if (hints.contains("x-canonical-secondary-icon") && hints.value("x-canonical-secondary-icon").toString() == "incoming-call") {
+ qDebug() << "Have a phone call notification. Ignoring it..." << app_name << app_icon;
+ } else {
+ qDebug() << "Notification received" << app_name << app_icon << actions << hints << expire_timeout;
+ Notification n(app_name);
+ if (app_name.contains("twitter")) {
+ n.setType(Notification::NotificationTypeTwitter);
+ n.setSourceName("Twitter");
+ } else if (app_name.contains("dekko")) {
+ n.setType(Notification::NotificationTypeEmail);
+ n.setSourceName("EMail");
+ } else if (app_name.toLower().contains("gmail")) {
+ n.setType(Notification::NotificationTypeGMail);
+ n.setSourceName("GMail");
+ } else if (app_name.contains("facebook")) {
+ n.setType(Notification::NotificationTypeFacebook);
+ n.setSourceName("Facebook");
+ } else if (app_name.contains("telegram")) {
+ n.setType(Notification::NotificationTypeTelegram);
+ n.setSourceName("Telegram");
+ } else if (app_name.toLower().contains("hangout")) {
+ n.setType(Notification::NotificationTypeHangout);
+ n.setSourceName("Hangout");
+ } else if (app_name.contains("indicator-datetime")) {
+ n.setType(Notification::NotificationTypeReminder);
+ n.setSourceName("reminders");
+ } else {
+ n.setType(Notification::NotificationTypeGeneric);
+ }
+ n.setSender(summary);
+ n.setBody(body);
+ foreach (const QString &action, actions) {
+ if (action.contains(QRegExp("^[a-z]*://"))) {
+ n.setActToken(action);
+ break;
+ }
+ }
+ qDebug() << "have act token" << n.actToken();
+
+ emit notificationReceived(n);
+ }
+ }
+ // We never return something. We're just snooping in...
+ setDelayedReply(true);
+ return 0;
+}
+
+void UbuntuPlatform::setupMusicService()
+{
+ if (!m_mprisService.isEmpty()) {
+ disconnect(this, SLOT(mediaPropertiesChanged(QString,QVariantMap,QStringList)));
+ }
+
+ QDBusConnectionInterface *iface = QDBusConnection::sessionBus().interface();
+ const QStringList &services = iface->registeredServiceNames();
+ foreach (QString service, services) {
+ if (service.startsWith("org.mpris.MediaPlayer2.")) {
+ qDebug() << "have mpris service" << service;
+ m_mprisService = service;
+ fetchMusicMetadata();
+ QDBusConnection::sessionBus().connect(m_mprisService, "/org/mpris/MediaPlayer2", "", "PropertiesChanged", this, SLOT(mediaPropertiesChanged(QString,QVariantMap,QStringList)));
+ break;
+ }
+ }
+}
+
+void UbuntuPlatform::sendMusicControlCommand(MusicControlButton controlButton)
+{
+ if (m_mprisService.isEmpty()) {
+ setupMusicService();
+ }
+
+ QString method;
+ switch (controlButton) {
+ case MusicControlPlayPause:
+ method = "PlayPause";
+ break;
+ case MusicControlSkipBack:
+ method = "Previous";
+ break;
+ case MusicControlSkipNext:
+ method = "Next";
+ break;
+ default:
+ ;
+ }
+
+ if (!method.isEmpty()) {
+ QDBusMessage call = QDBusMessage::createMethodCall(m_mprisService, "/org/mpris/MediaPlayer2", "org.mpris.MediaPlayer2.Player", method);
+ QDBusError err = QDBusConnection::sessionBus().call(call);
+
+ if (err.isValid()) {
+ qWarning() << "Error calling mpris method on" << m_mprisService << ":" << err.message();
+ }
+ return;
+ }
+
+ int volumeDiff = 0;
+ switch (controlButton) {
+ case MusicControlVolumeUp:
+ volumeDiff = 1;
+ break;
+ case MusicControlVolumeDown:
+ volumeDiff = -1;
+ break;
+ default:
+ ;
+ }
+
+ if (m_volumeAction && volumeDiff != 0) {
+ m_volumeAction->activate(volumeDiff);
+ return;
+ }
+}
+
+MusicMetaData UbuntuPlatform::musicMetaData() const
+{
+ return m_musicMetaData;
+}
+
+void UbuntuPlatform::hangupCall(uint cookie)
+{
+ m_telepathyMonitor->hangupCall(cookie);
+}
+
+QList<CalendarEvent> UbuntuPlatform::organizerItems() const
+{
+ return m_organizerAdapter->items();
+}
+
+void UbuntuPlatform::actionTriggered(const QString &actToken)
+{
+ url_dispatch_send(actToken.toStdString().c_str(), [] (const gchar *, gboolean, gpointer) {}, nullptr);
+}
+
+void UbuntuPlatform::fetchMusicMetadata()
+{
+ if (!m_mprisService.isEmpty()) {
+ QDBusMessage call = QDBusMessage::createMethodCall(m_mprisService, "/org/mpris/MediaPlayer2", "org.freedesktop.DBus.Properties", "Get");
+ call << "org.mpris.MediaPlayer2.Player" << "Metadata";
+ QDBusPendingCall pcall = QDBusConnection::sessionBus().asyncCall(call);
+ QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pcall, this);
+ connect(watcher, &QDBusPendingCallWatcher::finished, this, &UbuntuPlatform::fetchMusicMetadataFinished);
+ }
+}
+
+void UbuntuPlatform::fetchMusicMetadataFinished(QDBusPendingCallWatcher *watcher)
+{
+ watcher->deleteLater();
+ QDBusReply<QDBusVariant> reply = watcher->reply();
+ if (reply.isValid()) {
+ QVariantMap curMetadata = qdbus_cast<QVariantMap>(reply.value().variant().value<QDBusArgument>());
+ m_musicMetaData.artist = curMetadata.value("xesam:artist").toString();
+ m_musicMetaData.album = curMetadata.value("xesam:album").toString();
+ m_musicMetaData.title = curMetadata.value("xesam:title").toString();
+ emit musicMetadataChanged(m_musicMetaData);
+ } else {
+ qWarning() << reply.error().message();
+ }
+}
+
+void UbuntuPlatform::mediaPropertiesChanged(const QString &interface, const QVariantMap &changedProps, const QStringList &invalidatedProps)
+{
+ Q_UNUSED(interface)
+ Q_UNUSED(changedProps)
+ Q_UNUSED(invalidatedProps)
+ fetchMusicMetadata();
+}
diff --git a/rockworkd/platformintegration/ubuntu/ubuntuplatform.h b/rockworkd/platformintegration/ubuntu/ubuntuplatform.h
new file mode 100644
index 0000000..5679a42
--- /dev/null
+++ b/rockworkd/platformintegration/ubuntu/ubuntuplatform.h
@@ -0,0 +1,62 @@
+#ifndef UBUNTUPLATFORM_H
+#define UBUNTUPLATFORM_H
+
+#include "libpebble/platforminterface.h"
+#include "libpebble/enums.h"
+
+#include <QDBusInterface>
+#include <TelepathyQt/AbstractClientObserver>
+
+#include <qdbusactiongroup.h>
+#include <qstateaction.h>
+
+class QDBusPendingCallWatcher;
+class TelepathyMonitor;
+class OrganizerAdapter;
+class SyncMonitorClient;
+
+class UbuntuPlatform : public PlatformInterface, public QDBusContext
+{
+ Q_OBJECT
+ Q_CLASSINFO("D-Bus Interface", "org.freedesktop.Notifications")
+ Q_PROPERTY(QDBusInterface* interface READ interface)
+
+
+public:
+ UbuntuPlatform(QObject *parent = 0);
+ QDBusInterface* interface() const;
+
+ void sendMusicControlCommand(MusicControlButton controlButton) override;
+ MusicMetaData musicMetaData() const override;
+
+ void hangupCall(uint cookie) override;
+
+ QList<CalendarEvent> organizerItems() const override;
+
+ void actionTriggered(const QString &actToken) override;
+
+public slots:
+ 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);
+
+
+private slots:
+ void setupMusicService();
+ void fetchMusicMetadata();
+ void fetchMusicMetadataFinished(QDBusPendingCallWatcher *watcher);
+ void mediaPropertiesChanged(const QString &interface, const QVariantMap &changedProps, const QStringList &invalidatedProps);
+
+private:
+ QDBusInterface *m_iface;
+
+ QString m_mprisService;
+ MusicMetaData m_musicMetaData;
+ QDBusActionGroup m_volumeActionGroup;
+ QStateAction *m_volumeAction = nullptr;
+
+ TelepathyMonitor *m_telepathyMonitor;
+ OrganizerAdapter *m_organizerAdapter;
+ SyncMonitorClient *m_syncMonitorClient;
+ QTimer m_syncTimer;
+};
+
+#endif // UBUNTUPLATFORM_H