diff options
| -rw-r--r-- | app/pebble.desktop | 2 | ||||
| -rw-r--r-- | app/pebblefirmware.cpp | 18 | ||||
| -rw-r--r-- | app/qml/pages/ManagerPage.qml | 622 | ||||
| -rw-r--r-- | daemon/daemon.pro | 6 | ||||
| -rw-r--r-- | daemon/manager.cpp | 1185 | ||||
| -rw-r--r-- | daemon/manager.h | 317 | ||||
| -rw-r--r-- | daemon/notificationmanager.cpp | 120 | ||||
| -rw-r--r-- | daemon/notificationmanager.h | 3 | ||||
| -rw-r--r-- | daemon/settings.h | 170 | ||||
| -rw-r--r-- | daemon/timelineitem.cpp | 144 | ||||
| -rw-r--r-- | daemon/timelineitem.h | 194 | ||||
| -rw-r--r-- | daemon/watchconnector.cpp | 136 | ||||
| -rw-r--r-- | daemon/watchconnector.h | 610 |
13 files changed, 1985 insertions, 1542 deletions
diff --git a/app/pebble.desktop b/app/pebble.desktop index c7dd6c3..881941b 100644 --- a/app/pebble.desktop +++ b/app/pebble.desktop @@ -4,4 +4,4 @@ X-Nemo-Application-Type=silica-qt5 Name=Pebble Icon=pebble Exec=pebble %U -MimeType=application/zip; +MimeType=application/x-pebble-pkg; diff --git a/app/pebblefirmware.cpp b/app/pebblefirmware.cpp index 33460e5..e3bbc5a 100644 --- a/app/pebblefirmware.cpp +++ b/app/pebblefirmware.cpp @@ -16,16 +16,28 @@ PebbleFirmware::PebbleFirmware(QObject *parent) : void PebbleFirmware::updateLatest(QString hw) { QNetworkRequest req; - req.setUrl(firmwareURL.arg(hw).arg(hw.startsWith("snowy_") ? "release-v3" : "release-v2")); + req.setUrl(firmwareURL.arg(hw).arg("release-v3.8")); req.setRawHeader("Cache-Control", "no-cache"); qDebug() << "Getting latest firmware" << req.url(); nm->get(req); } -void PebbleFirmware::fetchFirmware(QString type) +void PebbleFirmware::fetchFirmware(QString currVer) { + QJsonObject targetFirmware; + if (_latest.contains("3.x-migration") && currVer < "v3.0.0") { + targetFirmware = _latest.value("3.x-migration").toObject(); + } else if (currVer >= "v3.0.0" && + _latest.value("normal").toObject().value("friendlyVersion").toString() > currVer){ + targetFirmware = _latest.value("normal").toObject(); + } + + if (targetFirmware.isEmpty()) { + qDebug() << "Watch firmware is up to date"; + return; + } QNetworkRequest req; - req.setUrl(_latest.value(type).toObject().value("url").toString()); + req.setUrl(targetFirmware.value("url").toString()); req.setRawHeader("Cache-Control", "no-cache"); qDebug() << "Fetching firmware" << req.url(); nm->get(req); diff --git a/app/qml/pages/ManagerPage.qml b/app/qml/pages/ManagerPage.qml index 8ff0db2..af5a6b2 100644 --- a/app/qml/pages/ManagerPage.qml +++ b/app/qml/pages/ManagerPage.qml @@ -1,301 +1,321 @@ -import QtQuick 2.0 -import QtQml 2.1 -import Sailfish.Silica 1.0 -import org.nemomobile.configuration 1.0 -import org.nemomobile.dbus 2.0 - -Page { - id: page - - ConfigurationGroup { - id: settings - path: "/org/pebbled/settings" - property string profileWhenConnected: "" - property string profileWhenDisconnected: "" - property bool transliterateMessage - property bool useSystemVolume - property bool incomingCallNotification - property bool notificationsCommhistoryd - property bool notificationsMissedCall - property bool notificationsEmails - property bool notificationsMitakuuluu - property bool notificationsTwitter - property bool notificationsFacebook - property bool notificationsOther - property bool notificationsAll - } - - DBusInterface { - id: profiled - - service: 'com.nokia.profiled' - iface: 'com.nokia.profiled' - path: '/com/nokia/profiled' - - property var profiles - } - - Component.onCompleted: { - profiled.typedCall('get_profiles', [], function (result) { - console.log('Got profiles: ' + result); - profiled.profiles = result; - }); - } - - - - SilicaFlickable { - id: flickable - anchors.fill: parent - contentHeight: column.height - - VerticalScrollDecorator { flickable: flickable } - - PullDownMenu { - MenuItem { - text: qsTr("Pebble Appstore") - onClicked: pageStack.push(Qt.resolvedUrl("AppStorePage.qml")) - } - MenuItem { - text: qsTr("About") - onClicked: pageStack.push(Qt.resolvedUrl("AboutPage.qml")) - } - } - - Column { - id: column - - width: page.width - spacing: Theme.paddingLarge - PageHeader { - title: qsTr("Pebble Manager") - } - - Label { - color: Theme.highlightColor - font.pixelSize: Theme.fontSizeSmall - visible: pebbled.active && !pebbled.connected - text: qsTr("Waiting for watch...\nIf it can't be found please check it's available and paired in Bluetooth settings.") - wrapMode: Text.Wrap - anchors { - left: parent.left - right: parent.right - margins: Theme.paddingLarge - } - - } - Button { - visible: pebbled.connected - text: pebbled.name - anchors { - left: parent.left - right: parent.right - margins: Theme.paddingLarge - } - onClicked: pageStack.push(Qt.resolvedUrl("WatchPage.qml")) - } - - Label { - text: qsTr("Service") - font.family: Theme.fontFamilyHeading - color: Theme.highlightColor - anchors.right: parent.right - anchors.rightMargin: Theme.paddingMedium - } - TextSwitch { - text: qsTr("Enabled") - description: pebbled.enabled ? qsTr("Automatic startup") : qsTr("Manual startup") - checked: pebbled.enabled - automaticCheck: false - onClicked: pebbled.setEnabled(!checked) - } - TextSwitch { - text: qsTr("Active") - description: pebbled.active ? qsTr("Running") : qsTr("Dead") - checked: pebbled.active - automaticCheck: false - onClicked: pebbled.setActive(!checked) - } - TextSwitch { - text: qsTr("Connection") - description: pebbled.connected ? qsTr("Connected"): qsTr("Disconnected") - checked: pebbled.connected - automaticCheck: false - onClicked: { - if (pebbled.connected) { - pebbled.disconnect(); - } else { - pebbled.reconnect(); - } - } - } - - Label { - text: qsTr("Settings") - font.family: Theme.fontFamilyHeading - color: Theme.highlightColor - anchors.right: parent.right - anchors.rightMargin: Theme.paddingMedium - } - TextSwitch { - text: qsTr("Forward phone calls") - checked: settings.incomingCallNotification - automaticCheck: false - onClicked: { - settings.incomingCallNotification = !settings.incomingCallNotification; - } - } - TextSwitch { - text: qsTr("Control main volume") - description: qsTr("Pebble music volume buttons change the main phone volume directly instead of through the music player") - checked: settings.useSystemVolume - automaticCheck: true - onClicked: { - settings.useSystemVolume = !settings.useSystemVolume; - } - } - TextSwitch { - text: qsTr("Transliterate messages") - description: qsTr("Messages are transliterated to ASCII before sending to Pebble") - checked: settings.transliterateMessage - automaticCheck: false - onClicked: { - settings.transliterateMessage = !settings.transliterateMessage; - } - } - - Label { - text: qsTr("Notifications") - font.family: Theme.fontFamilyHeading - color: Theme.highlightColor - anchors.right: parent.right - anchors.rightMargin: Theme.paddingMedium - } - - TextSwitch { - text: qsTr("Messaging") - description: qsTr("SMS and IM") - checked: settings.notificationsCommhistoryd - automaticCheck: false - onClicked: { - settings.notificationsCommhistoryd = !settings.notificationsCommhistoryd; - } - } - - TextSwitch { - text: qsTr("Missed call") - checked: settings.notificationsMissedCall - automaticCheck: false - onClicked: { - settings.notificationsMissedCall = !settings.notificationsMissedCall; - } - } - - TextSwitch { - text: qsTr("Emails") - checked: settings.notificationsEmails - automaticCheck: false - onClicked: { - settings.notificationsEmails = !settings.notificationsEmails; - } - } - - TextSwitch { - text: qsTr("Mitakuuluu") - checked: settings.notificationsMitakuuluu - automaticCheck: false - onClicked: { - settings.notificationsMitakuuluu = !settings.notificationsMitakuuluu; - } - } - - TextSwitch { - text: qsTr("Twitter") - checked: settings.notificationsTwitter - automaticCheck: false - onClicked: { - settings.notificationsTwitter = !settings.notificationsTwitter; - } - } - - TextSwitch { - visible: false //not yet supported - text: qsTr("Facebook") - checked: settings.notificationsFacebook - automaticCheck: false - onClicked: { - settings.notificationsFacebook = !settings.notificationsFacebook; - } - } - - TextSwitch { - text: qsTr("Other notifications") - checked: settings.notificationsOther - automaticCheck: false - onClicked: { - settings.notificationsOther = !settings.notificationsOther; - } - } - - TextSwitch { - text: qsTr("All notifications") - checked: settings.notificationsAll - automaticCheck: false - enabled: settings.notificationsOther - onClicked: { - settings.notificationsAll = !settings.notificationsAll; - } - } - - Label { - text: qsTr("Profiles") - font.family: Theme.fontFamilyHeading - color: Theme.highlightColor - anchors.right: parent.right - anchors.rightMargin: Theme.paddingMedium - } - - ComboBox { - label: qsTr("Connected") - menu: ContextMenu { - MenuItem { - text: qsTr("no change") - font.capitalization: Font.SmallCaps - } - Repeater { - model: profiled.profiles - delegate: MenuItem { - text: modelData - down: modelData === settings.profileWhenConnected - } - } - } - value: settings.profileWhenConnected || qsTr("no change") - onCurrentIndexChanged: { - settings.profileWhenConnected = currentIndex ? currentItem.text : "" - } - } - - ComboBox { - label: qsTr("Disconnected") - menu: ContextMenu { - MenuItem { - text: qsTr("no change") - font.capitalization: Font.SmallCaps - } - Repeater { - model: profiled.profiles - delegate: MenuItem { - text: modelData - down: modelData === settings.profileWhenDisconnected - } - } - } - value: settings.profileWhenDisconnected || qsTr("no change") - onCurrentIndexChanged: { - settings.profileWhenDisconnected = currentIndex ? currentItem.text : "" - } - } - } - } -} +import QtQuick 2.0
+import QtQml 2.1
+import Sailfish.Silica 1.0
+import org.nemomobile.configuration 1.0
+import org.nemomobile.dbus 2.0
+
+Page {
+ id: page
+
+ ConfigurationGroup {
+ id: settings
+ path: "/org/pebbled/settings"
+ property string profileWhenConnected: ""
+ property string profileWhenDisconnected: ""
+ property bool transliterateMessage
+ property bool useSystemVolume
+ property bool incomingCallNotification
+ property bool notificationsCommhistoryd
+ property bool notificationsMissedCall
+ property bool notificationsEmails
+ property bool notificationsTwitter
+ property bool notificationsFacebook
+ property bool notificationsTelegram
+ property bool notificationsHangouts
+ property bool notificationsWhatsapp
+ property bool notificationsOther
+ property bool notificationsAll
+ }
+
+ DBusInterface {
+ id: profiled
+
+ service: 'com.nokia.profiled'
+ iface: 'com.nokia.profiled'
+ path: '/com/nokia/profiled'
+
+ property var profiles
+ }
+
+ Component.onCompleted: {
+ profiled.typedCall('get_profiles', [], function (result) {
+ console.log('Got profiles: ' + result);
+ profiled.profiles = result;
+ });
+ }
+
+
+
+ SilicaFlickable {
+ id: flickable
+ anchors.fill: parent
+ contentHeight: column.height
+
+ VerticalScrollDecorator { flickable: flickable }
+
+ PullDownMenu {
+ MenuItem {
+ text: qsTr("Pebble Appstore")
+ onClicked: pageStack.push(Qt.resolvedUrl("AppStorePage.qml"))
+ }
+ MenuItem {
+ text: qsTr("About")
+ onClicked: pageStack.push(Qt.resolvedUrl("AboutPage.qml"))
+ }
+ }
+
+ Column {
+ id: column
+
+ width: page.width
+ spacing: Theme.paddingLarge
+ PageHeader {
+ title: qsTr("Pebble Manager")
+ }
+
+ Label {
+ color: Theme.highlightColor
+ font.pixelSize: Theme.fontSizeSmall
+ visible: pebbled.active && !pebbled.connected
+ text: qsTr("Waiting for watch...\nIf it can't be found please check it's available and paired in Bluetooth settings.")
+ wrapMode: Text.Wrap
+ anchors {
+ left: parent.left
+ right: parent.right
+ margins: Theme.paddingLarge
+ }
+
+ }
+ Button {
+ visible: pebbled.connected
+ text: pebbled.name
+ anchors {
+ left: parent.left
+ right: parent.right
+ margins: Theme.paddingLarge
+ }
+ onClicked: pageStack.push(Qt.resolvedUrl("WatchPage.qml"))
+ }
+
+ Label {
+ text: qsTr("Service")
+ font.family: Theme.fontFamilyHeading
+ color: Theme.highlightColor
+ anchors.right: parent.right
+ anchors.rightMargin: Theme.paddingMedium
+ }
+ TextSwitch {
+ text: qsTr("Enabled")
+ description: pebbled.enabled ? qsTr("Automatic startup") : qsTr("Manual startup")
+ checked: pebbled.enabled
+ automaticCheck: false
+ onClicked: pebbled.setEnabled(!checked)
+ }
+ TextSwitch {
+ text: qsTr("Active")
+ description: pebbled.active ? qsTr("Running") : qsTr("Dead")
+ checked: pebbled.active
+ automaticCheck: false
+ onClicked: pebbled.setActive(!checked)
+ }
+ TextSwitch {
+ text: qsTr("Connection")
+ description: pebbled.connected ? qsTr("Connected"): qsTr("Disconnected")
+ checked: pebbled.connected
+ automaticCheck: false
+ onClicked: {
+ if (pebbled.connected) {
+ pebbled.disconnect();
+ } else {
+ pebbled.reconnect();
+ }
+ }
+ }
+
+ Label {
+ text: qsTr("Settings")
+ font.family: Theme.fontFamilyHeading
+ color: Theme.highlightColor
+ anchors.right: parent.right
+ anchors.rightMargin: Theme.paddingMedium
+ }
+ TextSwitch {
+ text: qsTr("Forward phone calls")
+ checked: settings.incomingCallNotification
+ automaticCheck: false
+ onClicked: {
+ settings.incomingCallNotification = !settings.incomingCallNotification;
+ }
+ }
+ TextSwitch {
+ text: qsTr("Control main volume")
+ description: qsTr("Pebble music volume buttons change the main phone volume directly instead of through the music player")
+ checked: settings.useSystemVolume
+ automaticCheck: true
+ onClicked: {
+ settings.useSystemVolume = !settings.useSystemVolume;
+ }
+ }
+ TextSwitch {
+ text: qsTr("Transliterate messages")
+ description: qsTr("Messages are transliterated to ASCII before sending to Pebble")
+ checked: settings.transliterateMessage
+ automaticCheck: false
+ onClicked: {
+ settings.transliterateMessage = !settings.transliterateMessage;
+ }
+ }
+
+ Label {
+ text: qsTr("Notifications")
+ font.family: Theme.fontFamilyHeading
+ color: Theme.highlightColor
+ anchors.right: parent.right
+ anchors.rightMargin: Theme.paddingMedium
+ }
+
+ TextSwitch {
+ text: qsTr("Messaging")
+ description: qsTr("SMS and IM")
+ checked: settings.notificationsCommhistoryd
+ automaticCheck: false
+ onClicked: {
+ settings.notificationsCommhistoryd = !settings.notificationsCommhistoryd;
+ }
+ }
+
+ TextSwitch {
+ text: qsTr("Missed call")
+ checked: settings.notificationsMissedCall
+ automaticCheck: false
+ onClicked: {
+ settings.notificationsMissedCall = !settings.notificationsMissedCall;
+ }
+ }
+
+ TextSwitch {
+ text: qsTr("Emails")
+ checked: settings.notificationsEmails
+ automaticCheck: false
+ onClicked: {
+ settings.notificationsEmails = !settings.notificationsEmails;
+ }
+ }
+
+ TextSwitch {
+ text: qsTr("Telegram")
+ checked: settings.notificationsTelegram
+ automaticCheck: false
+ onClicked: {
+ settings.notificationsTelegram = !settings.notificationsTelegram;
+ }
+ }
+
+ TextSwitch {
+ text: qsTr("Whatsapp")
+ checked: settings.notificationsWhatsapp
+ automaticCheck: false
+ onClicked: {
+ settings.notificationsWhatsapp = !settings.notificationsWhatsapp;
+ }
+ }
+
+ TextSwitch {
+ text: qsTr("Hangouts")
+ checked: settings.notificationsHangouts
+ automaticCheck: false
+ onClicked: {
+ settings.notificationsHangouts = !settings.notificationsHangouts;
+ }
+ }
+
+ TextSwitch {
+ text: qsTr("Twitter")
+ checked: settings.notificationsTwitter
+ automaticCheck: false
+ onClicked: {
+ settings.notificationsTwitter = !settings.notificationsTwitter;
+ }
+ }
+
+ TextSwitch {
+ visible: false //not yet supported
+ text: qsTr("Facebook")
+ checked: settings.notificationsFacebook
+ automaticCheck: false
+ onClicked: {
+ settings.notificationsFacebook = !settings.notificationsFacebook;
+ }
+ }
+
+ TextSwitch {
+ text: qsTr("Other notifications")
+ checked: settings.notificationsOther
+ automaticCheck: false
+ onClicked: {
+ settings.notificationsOther = !settings.notificationsOther;
+ }
+ }
+
+ TextSwitch {
+ text: qsTr("All notifications")
+ checked: settings.notificationsAll
+ automaticCheck: false
+ enabled: settings.notificationsOther
+ onClicked: {
+ settings.notificationsAll = !settings.notificationsAll;
+ }
+ }
+
+ Label {
+ text: qsTr("Profiles")
+ font.family: Theme.fontFamilyHeading
+ color: Theme.highlightColor
+ anchors.right: parent.right
+ anchors.rightMargin: Theme.paddingMedium
+ }
+
+ ComboBox {
+ label: qsTr("Connected")
+ menu: ContextMenu {
+ MenuItem {
+ text: qsTr("no change")
+ font.capitalization: Font.SmallCaps
+ }
+ Repeater {
+ model: profiled.profiles
+ delegate: MenuItem {
+ text: modelData
+ down: modelData === settings.profileWhenConnected
+ }
+ }
+ }
+ value: settings.profileWhenConnected || qsTr("no change")
+ onCurrentIndexChanged: {
+ settings.profileWhenConnected = currentIndex ? currentItem.text : ""
+ }
+ }
+
+ ComboBox {
+ label: qsTr("Disconnected")
+ menu: ContextMenu {
+ MenuItem {
+ text: qsTr("no change")
+ font.capitalization: Font.SmallCaps
+ }
+ Repeater {
+ model: profiled.profiles
+ delegate: MenuItem {
+ text: modelData
+ down: modelData === settings.profileWhenDisconnected
+ }
+ }
+ }
+ value: settings.profileWhenDisconnected || qsTr("no change")
+ onCurrentIndexChanged: {
+ settings.profileWhenDisconnected = currentIndex ? currentItem.text : ""
+ }
+ }
+ }
+ }
+}
diff --git a/daemon/daemon.pro b/daemon/daemon.pro index 4520b57..9bdc643 100644 --- a/daemon/daemon.pro +++ b/daemon/daemon.pro @@ -27,7 +27,8 @@ SOURCES += \ packer.cpp \ bankmanager.cpp \ uploadmanager.cpp \ - bundle.cpp + bundle.cpp \ + timelineitem.cpp HEADERS += \ manager.h \ @@ -47,7 +48,8 @@ HEADERS += \ packer.h \ bankmanager.h \ uploadmanager.h \ - bundle.h + bundle.h \ + timelineitem.h DBUS_ADAPTORS += ../org.pebbled.Watch.xml diff --git a/daemon/manager.cpp b/daemon/manager.cpp index 37625e3..4d6a525 100644 --- a/daemon/manager.cpp +++ b/daemon/manager.cpp @@ -1,578 +1,607 @@ -#include <QDebug> -#include <QtContacts/QContact> -#include <QtContacts/QContactPhoneNumber> - -#include "manager.h" -#include "watch_adaptor.h" -#include "bundle.h" - -Manager::Manager(Settings *settings, QObject *parent) : - QObject(parent), l(metaObject()->className()), settings(settings), - proxy(new PebbledProxy(this)), - watch(new WatchConnector(this)), - upload(new UploadManager(watch, this)), - apps(new AppManager(this)), - bank(new BankManager(watch, upload, apps, this)), - voice(new VoiceCallManager(settings, this)), - notifications(new NotificationManager(settings, this)), - music(new MusicManager(watch, settings, this)), - datalog(new DataLogManager(watch, this)), - appmsg(new AppMsgManager(apps, watch, this)), - js(new JSKitManager(watch, apps, appmsg, settings, this)), - notification(MNotification::DeviceEvent) -{ - connect(settings, SIGNAL(valueChanged(QString)), SLOT(onSettingChanged(const QString&))); - connect(settings, SIGNAL(valuesChanged()), SLOT(onSettingsChanged())); - - // We don't need to handle presence changes, so report them separately and ignore them - QMap<QString, QString> parameters; - parameters.insert(QString::fromLatin1("mergePresenceChanges"), QString::fromLatin1("false")); - contacts = new QContactManager("", parameters, this); - - numberFilter.setDetailType(QContactDetail::TypePhoneNumber, QContactPhoneNumber::FieldNumber); - numberFilter.setMatchFlags(QContactFilter::MatchPhoneNumber); - - connect(watch, SIGNAL(connectedChanged()), SLOT(onConnectedChanged())); - watch->setEndpointHandler(WatchConnector::watchPHONE_VERSION, - [this](const QByteArray& data) { - Q_UNUSED(data); - watch->sendPhoneVersion(); - return true; - }); - watch->setEndpointHandler(WatchConnector::watchPHONE_CONTROL, - [this](const QByteArray &data) { - if (data.at(0) == WatchConnector::callHANGUP) { - voice->hangupAll(); - } - return true; - }); - - connect(voice, SIGNAL(activeVoiceCallChanged()), SLOT(onActiveVoiceCallChanged())); - connect(voice, SIGNAL(error(const QString &)), SLOT(onVoiceError(const QString &))); - - connect(notifications, SIGNAL(error(const QString &)), SLOT(onNotifyError(const QString &))); - connect(notifications, SIGNAL(emailNotify(const QString &,const QString &,const QString &)), SLOT(onEmailNotify(const QString &,const QString &,const QString &))); - connect(notifications, SIGNAL(smsNotify(const QString &,const QString &)), SLOT(onSmsNotify(const QString &,const QString &))); - connect(notifications, SIGNAL(twitterNotify(const QString &,const QString &)), SLOT(onTwitterNotify(const QString &,const QString &))); - connect(notifications, SIGNAL(facebookNotify(const QString &,const QString &)), SLOT(onFacebookNotify(const QString &,const QString &))); - - connect(appmsg, &AppMsgManager::appStarted, this, &Manager::onAppOpened); - connect(appmsg, &AppMsgManager::appStopped, this, &Manager::onAppClosed); - - connect(js, &JSKitManager::appNotification, this, &Manager::onAppNotification); - - QDBusConnection session = QDBusConnection::sessionBus(); - new WatchAdaptor(proxy); - session.registerObject("/org/pebbled/Watch", proxy); - session.registerService("org.pebbled"); - - connect(watch, &WatchConnector::pebbleChanged, proxy, &PebbledProxy::NameChanged); - connect(watch, &WatchConnector::pebbleChanged, proxy, &PebbledProxy::AddressChanged); - connect(watch, &WatchConnector::connectedChanged, proxy, &PebbledProxy::ConnectedChanged); - connect(watch, &WatchConnector::versionsChanged, proxy, &PebbledProxy::InfoChanged); - connect(bank, &BankManager::slotsChanged, proxy, &PebbledProxy::AppSlotsChanged); - connect(apps, &AppManager::appsChanged, proxy, &PebbledProxy::AllAppsChanged); - - connect(watch, SIGNAL(connectedChanged()), SLOT(applyProfile())); - - // Set BT icon for notification - notification.setImage("icon-system-bluetooth-device"); - - watch->findPebbles(); -} - -Manager::~Manager() -{ -} - -void Manager::onSettingChanged(const QString &key) -{ - qCDebug(l) << __FUNCTION__ << key << ":" << settings->property(qPrintable(key)); -} - -void Manager::onSettingsChanged() -{ - qCWarning(l) << __FUNCTION__ << "Not implemented!"; -} - -void Manager::onConnectedChanged() -{ - QString message = QString("%1 %2") - .arg(watch->name().isEmpty() ? "Pebble" : watch->name()) - .arg(watch->isConnected() ? "connected" : "disconnected"); - qCDebug(l) << message; - - if (notification.isPublished()) notification.remove(); - - notification.setBody(message); - if (!notification.publish()) { - qCDebug(l) << "Failed publishing notification"; - } -} - -void Manager::onActiveVoiceCallChanged() -{ - qCDebug(l) << "Manager::onActiveVoiceCallChanged()"; - - if (!settings->property("incomingCallNotification").toBool()) { - qCDebug(l) << "Ignoring ActiveVoiceCallChanged because of setting!"; - return; - } - - VoiceCallHandler* handler = voice->activeVoiceCall(); - if (handler) { - connect(handler, SIGNAL(statusChanged()), SLOT(onActiveVoiceCallStatusChanged())); - connect(handler, SIGNAL(destroyed()), SLOT(onActiveVoiceCallStatusChanged())); - if (handler->status()) onActiveVoiceCallStatusChanged(); - } -} - -void Manager::onActiveVoiceCallStatusChanged() -{ - VoiceCallHandler* handler = voice->activeVoiceCall(); - if (!handler) { - qCDebug(l) << "ActiveVoiceCall destroyed"; - watch->endPhoneCall(); - return; - } - - qCDebug(l) << "handlerId:" << handler->handlerId() - << "providerId:" << handler->providerId() - << "status:" << handler->status() - << "statusText:" << handler->statusText() - << "lineId:" << handler->lineId() - << "incoming:" << handler->isIncoming(); - - if (!watch->isConnected()) { - qCDebug(l) << "Watch is not connected"; - return; - } - - switch ((VoiceCallHandler::VoiceCallStatus)handler->status()) { - case VoiceCallHandler::STATUS_ALERTING: - case VoiceCallHandler::STATUS_DIALING: - qCDebug(l) << "Tell outgoing:" << handler->lineId(); - watch->ring(handler->lineId(), findPersonByNumber(handler->lineId()), false); - break; - case VoiceCallHandler::STATUS_INCOMING: - case VoiceCallHandler::STATUS_WAITING: - qCDebug(l) << "Tell incoming:" << handler->lineId(); - watch->ring(handler->lineId(), findPersonByNumber(handler->lineId())); - break; - case VoiceCallHandler::STATUS_NULL: - case VoiceCallHandler::STATUS_DISCONNECTED: - qCDebug(l) << "Endphone"; - watch->endPhoneCall(); - break; - case VoiceCallHandler::STATUS_ACTIVE: - qCDebug(l) << "Startphone"; - watch->startPhoneCall(); - break; - case VoiceCallHandler::STATUS_HELD: - break; - } -} - -QString Manager::findPersonByNumber(QString number) -{ - QString person; - numberFilter.setValue(number); - - const QList<QContact> &found = contacts->contacts(numberFilter); - if (found.size() == 1) { - person = found[0].detail(QContactDetail::TypeDisplayLabel).value(0).toString(); - } - - if (settings->property("transliterateMessage").toBool()) { - transliterateMessage(person); - } - return person; -} - -void Manager::onVoiceError(const QString &message) -{ - qCCritical(l) << "Error:" << message; -} - - -void Manager::onNotifyError(const QString &message) -{ - qWarning() << "Error:" << message; -} - -void Manager::onSmsNotify(const QString &sender, const QString &data) -{ - if (settings->property("transliterateMessage").toBool()) { - transliterateMessage(sender); - transliterateMessage(data); - } - watch->sendSMSNotification(sender, data); -} - -void Manager::onTwitterNotify(const QString &sender, const QString &data) -{ - if (settings->property("transliterateMessage").toBool()) { - transliterateMessage(sender); - transliterateMessage(data); - } - watch->sendTwitterNotification(sender, data); -} - - -void Manager::onFacebookNotify(const QString &sender, const QString &data) -{ - if (settings->property("transliterateMessage").toBool()) { - transliterateMessage(sender); - transliterateMessage(data); - } - watch->sendFacebookNotification(sender, data); -} - - -void Manager::onEmailNotify(const QString &sender, const QString &data,const QString &subject) -{ - if (settings->property("transliterateMessage").toBool()) { - transliterateMessage(sender); - transliterateMessage(data); - transliterateMessage(subject); - } - watch->sendEmailNotification(sender, data, subject); -} - -void Manager::applyProfile() -{ - QString newProfile = settings->property( - watch->isConnected() ? "profileWhenConnected" - : "profileWhenDisconnected").toString(); - if (!newProfile.isEmpty()) { - QDBusReply<bool> res = QDBusConnection::sessionBus().call( - QDBusMessage::createMethodCall("com.nokia.profiled", "/com/nokia/profiled", "com.nokia.profiled", "set_profile") - << newProfile); - if (res.isValid()) { - if (!res.value()) { - qCCritical(l) << "Unable to set profile" << newProfile; - } - } - else { - qCCritical(l) << res.error().message(); - } - } -} - -void Manager::ping(uint val) -{ - qCDebug(l) << "ping" << val; - - if (settings->property("debug").toBool()) { - // magic here! - // I do not want to add specific debugging methods to pebbled - // so just provide some magic Ping() method handling here :-) - switch (val) { - case 128: - watch->sendSMSNotification("SMS", "lorem ipsum"); - return; - case 129: - watch->sendEmailNotification("e-mail", "lorem ipsum", "subject"); - return; - case 130: - watch->sendFacebookNotification("Facebook", "lorem ipsum"); - return; - case 131: - watch->sendTwitterNotification("Twitter", "lorem ipsum"); - return; - case 132: - watch->sendMusicNowPlaying("artist", "album", "track name"); - return; - } - } - - watch->ping(val); -} - -void Manager::transliterateMessage(const QString &text) -{ - if (transliterator.isNull()) { - UErrorCode status = U_ZERO_ERROR; - transliterator.reset(icu::Transliterator::createInstance(icu::UnicodeString::fromUTF8("Any-Latin; Latin-ASCII"),UTRANS_FORWARD, status)); - if (U_FAILURE(status)) { - qCWarning(l) << "Error creaing ICU Transliterator \"Any-Latin; Latin-ASCII\":" << u_errorName(status); - } - } - if (!transliterator.isNull()) { - qCDebug(l) << "String before transliteration:" << text; - - icu::UnicodeString uword = icu::UnicodeString::fromUTF8(text.toStdString()); - transliterator->transliterate(uword); - - std::string translited; - uword.toUTF8String(translited); - - const_cast<QString&>(text) = QString::fromStdString(translited); - qCDebug(l) << "String after transliteration:" << text; - } -} - -void Manager::onAppNotification(const QUuid &uuid, const QString &title, const QString &body) -{ - Q_UNUSED(uuid); - watch->sendSMSNotification(title, body); -} - -void Manager::onAppOpened(const QUuid &uuid) -{ - currentAppUuid = uuid; - emit proxy->AppUuidChanged(); - emit proxy->AppOpened(uuid.toString()); -} - -void Manager::onAppClosed(const QUuid &uuid) -{ - currentAppUuid = QUuid(); - emit proxy->AppClosed(uuid.toString()); - emit proxy->AppUuidChanged(); -} - -bool Manager::uploadFirmware(bool recovery, const QString &file) -{ - qCDebug(l) << "about to upload" << (recovery ? "recovery" : "normal") - << "firmware:" << file; - - Bundle bundle(Bundle::fromPath(file)); - if (!bundle.isValid()) { - qCWarning(l) << file << "is invalid bundle"; - return false; - } - - if (!bundle.fileExists(Bundle::FIRMWARE) || !bundle.fileExists(Bundle::RESOURCES)) { - qCWarning(l) << file << "is missing binary or resource"; - return false; - } - - watch->systemMessage(WatchConnector::systemFIRMWARE_START, - [this, recovery, bundle](const QByteArray &data) { - qCDebug(l) << "got response to firmware start" << data.toHex(); - - QSharedPointer<QIODevice> resourceFile(bundle.openFile(Bundle::RESOURCES)); - if (!resourceFile) { - qCWarning(l) << "failed to open" << bundle.path() << "resource"; - watch->systemMessage(WatchConnector::systemFIRMWARE_FAIL); - return true; - } - - upload->uploadFirmwareResources(resourceFile.data(), bundle.crcFile(Bundle::RESOURCES), - [this, recovery, bundle, resourceFile]() { - qCDebug(l) << "firmware resource upload succesful"; - resourceFile->close(); - // Proceed to upload the resource file - QSharedPointer<QIODevice> binaryFile(bundle.openFile(Bundle::FIRMWARE)); - if (binaryFile) { - upload->uploadFirmwareBinary(recovery, binaryFile.data(), bundle.crcFile(Bundle::FIRMWARE), - [this, binaryFile]() { - binaryFile->close(); - qCDebug(l) << "firmware binary upload succesful"; - // Upload succesful - watch->systemMessage(WatchConnector::systemFIRMWARE_COMPLETE); - }, [this, binaryFile](int code) { - binaryFile->close(); - qCWarning(l) << "firmware binary upload failed" << code; - watch->systemMessage(WatchConnector::systemFIRMWARE_FAIL); - }); - } else { - qCWarning(l) << "failed to open" << bundle.path() << "binary"; - watch->systemMessage(WatchConnector::systemFIRMWARE_FAIL); - } - }, [this, resourceFile](int code) { - resourceFile->close(); - qCWarning(l) << "firmware resource upload failed" << code; - watch->systemMessage(WatchConnector::systemFIRMWARE_FAIL); - }); - - return true; - }); - - return true; -} - -QStringList PebbledProxy::AppSlots() const -{ - const int num_slots = manager()->bank->numSlots(); - QStringList l; - l.reserve(num_slots); - - for (int i = 0; i < num_slots; ++i) { - if (manager()->bank->isUsed(i)) { - QUuid uuid = manager()->bank->appAt(i); - l.append(uuid.toString()); - } else { - l.append(QString()); - } - } - - Q_ASSERT(l.size() == num_slots); - - return l; -} - -QVariantList PebbledProxy::AllApps() const -{ - QList<QUuid> uuids = manager()->apps->appUuids(); - QVariantList l; - - foreach (const QUuid &uuid, uuids) { - AppInfo info = manager()->apps->info(uuid); - QVariantMap m; - m.insert("local", QVariant::fromValue(info.isLocal())); - m.insert("uuid", QVariant::fromValue(info.uuid().toString())); - m.insert("short-name", QVariant::fromValue(info.shortName())); - m.insert("long-name", QVariant::fromValue(info.longName())); - m.insert("company-name", QVariant::fromValue(info.companyName())); - m.insert("version-label", QVariant::fromValue(info.versionLabel())); - m.insert("is-watchface", QVariant::fromValue(info.isWatchface())); - m.insert("configurable", QVariant::fromValue(info.capabilities().testFlag(AppInfo::Capability::Configurable))); - m.insert("path", QVariant::fromValue(info.path())); - - if (!info.getMenuIconImage().isNull()) { - m.insert("menu-icon", QVariant::fromValue(info.getMenuIconPng())); - } - - l.append(QVariant::fromValue(m)); - } - - return l; -} - -bool PebbledProxy::SendAppMessage(const QString &uuid, const QVariantMap &data) -{ - Q_ASSERT(calledFromDBus()); - const QDBusMessage msg = message(); - setDelayedReply(true); - manager()->appmsg->send(uuid, data, [this, msg]() { - QDBusMessage reply = msg.createReply(QVariant::fromValue(true)); - this->connection().send(reply); - }, [this, msg]() { - QDBusMessage reply = msg.createReply(QVariant::fromValue(false)); - this->connection().send(reply); - }); - return false; // D-Bus clients should never see this reply. -} - -QString PebbledProxy::StartAppConfiguration(const QString &uuid) -{ - Q_ASSERT(calledFromDBus()); - const QDBusMessage msg = message(); - QDBusConnection conn = connection(); - - if (manager()->currentAppUuid != uuid) { - qCWarning(l) << "Called StartAppConfiguration but the uuid" << uuid << "is not running"; - sendErrorReply(msg.interface() + ".Error.AppNotRunning", - "The requested app is not currently opened in the watch"); - return QString(); - } - - if (!manager()->js->isJSKitAppRunning()) { - qCWarning(l) << "Called StartAppConfiguration but the uuid" << uuid << "is not a JS app"; - sendErrorReply(msg.interface() + ".Error.JSNotActive", - "The requested app is not a PebbleKit JS application"); - return QString(); - } - - // After calling showConfiguration() on the script, - // it will (eventually!) return a URL to us via the appOpenUrl signal. - - // So we can't send the D-Bus reply right now. - setDelayedReply(true); - - // Set up a signal handler to catch the appOpenUrl signal. - QMetaObject::Connection *c = new QMetaObject::Connection; - *c = connect(manager()->js, &JSKitManager::appOpenUrl, - this, [this,conn,msg,c](const QUrl &url) { - // Workaround: due to a GCC crash we can't capture the uuid parameter, but we can extract - // it again from the original message arguments. - // Suspect GCC bug# is 59195, 61233, or 61321. - // TODO Possibly fixed in 4.9.0 - const QString uuid = msg.arguments().at(0).toString(); - - if (manager()->currentAppUuid != uuid) { - // App was changed while we were waiting for the script.. - QDBusMessage reply = msg.createErrorReply(msg.interface() + ".Error.AppNotRunning", - "The requested app is not currently opened in the watch"); - conn.send(reply); - } else { - QDBusMessage reply = msg.createReply(QVariant::fromValue(url.toString())); - conn.send(reply); - } - - disconnect(*c); - delete c; - }); - - // TODO: JS script may fail, never call OpenURL, or something like that - // In those cases we WILL leak the above connection. - // (at least until the next appOpenURL event comes in) - // So we need to also set a timeout or similar. - - manager()->js->showConfiguration(); - - // Note that the above signal handler _might_ have been already called by this point. - - return QString(); // This return value should never be used. -} - -void PebbledProxy::SendAppConfigurationData(const QString &uuid, const QString &data) -{ - Q_ASSERT(calledFromDBus()); - const QDBusMessage msg = message(); - - if (manager()->currentAppUuid != uuid) { - sendErrorReply(msg.interface() + ".Error.AppNotRunning", - "The requested app is not currently opened in the watch"); - return; - } - - if (!manager()->js->isJSKitAppRunning()) { - sendErrorReply(msg.interface() + ".Error.JSNotActive", - "The requested app is not a PebbleKit JS application"); - return; - } - - manager()->js->handleWebviewClosed(data); -} - -void PebbledProxy::UnloadApp(int slot) -{ - Q_ASSERT(calledFromDBus()); - const QDBusMessage msg = message(); - - if (!manager()->bank->unloadApp(slot)) { - sendErrorReply(msg.interface() + ".Error.CannotUnload", - "Cannot unload application"); - } -} - -void PebbledProxy::UploadApp(const QString &uuid, int slot) -{ - Q_ASSERT(calledFromDBus()); - const QDBusMessage msg = message(); - - if (!manager()->bank->uploadApp(QUuid(uuid), slot)) { - sendErrorReply(msg.interface() + ".Error.CannotUpload", - "Cannot upload application"); - } -} - -void PebbledProxy::NotifyFirmware(bool ok) -{ - Q_ASSERT(calledFromDBus()); - manager()->watch->sendFirmwareState(ok); -} - -void PebbledProxy::UploadFirmware(bool recovery, const QString &file) -{ - Q_ASSERT(calledFromDBus()); - const QDBusMessage msg = message(); - - if (!manager()->uploadFirmware(recovery, file)) { - sendErrorReply(msg.interface() + ".Error.CannotUpload", - "Cannot upload firmware"); - } -} +#include <QDebug>
+#include <QtContacts/QContact>
+#include <QtContacts/QContactPhoneNumber>
+
+#include "manager.h"
+#include "watch_adaptor.h"
+#include "bundle.h"
+
+Manager::Manager(Settings *settings, QObject *parent) :
+ QObject(parent), l(metaObject()->className()), settings(settings),
+ proxy(new PebbledProxy(this)),
+ watch(new WatchConnector(this)),
+ upload(new UploadManager(watch, this)),
+ apps(new AppManager(this)),
+ bank(new BankManager(watch, upload, apps, this)),
+ voice(new VoiceCallManager(settings, this)),
+ notifications(new NotificationManager(settings, this)),
+ music(new MusicManager(watch, settings, this)),
+ datalog(new DataLogManager(watch, this)),
+ appmsg(new AppMsgManager(apps, watch, this)),
+ js(new JSKitManager(watch, apps, appmsg, settings, this)),
+ notification(MNotification::DeviceEvent)
+{
+ connect(settings, SIGNAL(valueChanged(QString)), SLOT(onSettingChanged(const QString&)));
+ connect(settings, SIGNAL(valuesChanged()), SLOT(onSettingsChanged()));
+
+ // We don't need to handle presence changes, so report them separately and ignore them
+ QMap<QString, QString> parameters;
+ parameters.insert(QString::fromLatin1("mergePresenceChanges"), QString::fromLatin1("false"));
+ contacts = new QContactManager("", parameters, this);
+
+ numberFilter.setDetailType(QContactDetail::TypePhoneNumber, QContactPhoneNumber::FieldNumber);
+ numberFilter.setMatchFlags(QContactFilter::MatchPhoneNumber);
+
+ connect(watch, SIGNAL(connectedChanged()), SLOT(onConnectedChanged()));
+ watch->setEndpointHandler(WatchConnector::watchPHONE_VERSION,
+ [this](const QByteArray& data) {
+ Q_UNUSED(data);
+ watch->sendPhoneVersion();
+ return true;
+ });
+ watch->setEndpointHandler(WatchConnector::watchPHONE_CONTROL,
+ [this](const QByteArray &data) {
+ if (data.at(0) == WatchConnector::callHANGUP) {
+ voice->hangupAll();
+ }
+ return true;
+ });
+
+ connect(voice, SIGNAL(activeVoiceCallChanged()), SLOT(onActiveVoiceCallChanged()));
+ connect(voice, SIGNAL(error(const QString &)), SLOT(onVoiceError(const QString &)));
+
+ connect(notifications, SIGNAL(error(const QString &)), SLOT(onNotifyError(const QString &)));
+ connect(notifications, SIGNAL(emailNotify(const QString &,const QString &,const QString &)), SLOT(onEmailNotify(const QString &,const QString &,const QString &)));
+ connect(notifications, SIGNAL(smsNotify(const QString &,const QString &)), SLOT(onSmsNotify(const QString &,const QString &)));
+ connect(notifications, SIGNAL(twitterNotify(const QString &,const QString &)), SLOT(onTwitterNotify(const QString &,const QString &)));
+ connect(notifications, SIGNAL(facebookNotify(const QString &,const QString &)), SLOT(onFacebookNotify(const QString &,const QString &)));
+ connect(notifications, SIGNAL(telegramNotify(const QString &,const QString &)), SLOT(onTelegramNotify(const QString &,const QString &)));
+ connect(notifications, SIGNAL(hangoutsNotify(const QString &,const QString &)), SLOT(onHangoutsNotify(const QString &,const QString &)));
+ connect(notifications, SIGNAL(whatappNotify(const QString &,const QString &)), SLOT(onWhatsappNotify(const QString &,const QString &)));
+
+ connect(appmsg, &AppMsgManager::appStarted, this, &Manager::onAppOpened);
+ connect(appmsg, &AppMsgManager::appStopped, this, &Manager::onAppClosed);
+
+ connect(js, &JSKitManager::appNotification, this, &Manager::onAppNotification);
+
+ QDBusConnection session = QDBusConnection::sessionBus();
+ new WatchAdaptor(proxy);
+ session.registerObject("/org/pebbled/Watch", proxy);
+ session.registerService("org.pebbled");
+
+ connect(watch, &WatchConnector::pebbleChanged, proxy, &PebbledProxy::NameChanged);
+ connect(watch, &WatchConnector::pebbleChanged, proxy, &PebbledProxy::AddressChanged);
+ connect(watch, &WatchConnector::connectedChanged, proxy, &PebbledProxy::ConnectedChanged);
+ connect(watch, &WatchConnector::versionsChanged, proxy, &PebbledProxy::InfoChanged);
+ connect(bank, &BankManager::slotsChanged, proxy, &PebbledProxy::AppSlotsChanged);
+ connect(apps, &AppManager::appsChanged, proxy, &PebbledProxy::AllAppsChanged);
+
+ connect(watch, SIGNAL(connectedChanged()), SLOT(applyProfile()));
+
+ // Set BT icon for notification
+ notification.setImage("icon-system-bluetooth-device");
+
+ watch->findPebbles();
+}
+
+Manager::~Manager()
+{
+}
+
+void Manager::onSettingChanged(const QString &key)
+{
+ qCDebug(l) << __FUNCTION__ << key << ":" << settings->property(qPrintable(key));
+}
+
+void Manager::onSettingsChanged()
+{
+ qCWarning(l) << __FUNCTION__ << "Not implemented!";
+}
+
+void Manager::onConnectedChanged()
+{
+ QString message = QString("%1 %2")
+ .arg(watch->name().isEmpty() ? "Pebble" : watch->name())
+ .arg(watch->isConnected() ? "connected" : "disconnected");
+ qCDebug(l) << message;
+
+ if (notification.isPublished()) notification.remove();
+
+ notification.setBody(message);
+ if (!notification.publish()) {
+ qCDebug(l) << "Failed publishing notification";
+ }
+}
+
+void Manager::onActiveVoiceCallChanged()
+{
+ qCDebug(l) << "Manager::onActiveVoiceCallChanged()";
+
+ if (!settings->property("incomingCallNotification").toBool()) {
+ qCDebug(l) << "Ignoring ActiveVoiceCallChanged because of setting!";
+ return;
+ }
+
+ VoiceCallHandler* handler = voice->activeVoiceCall();
+ if (handler) {
+ connect(handler, SIGNAL(statusChanged()), SLOT(onActiveVoiceCallStatusChanged()));
+ connect(handler, SIGNAL(destroyed()), SLOT(onActiveVoiceCallStatusChanged()));
+ if (handler->status()) onActiveVoiceCallStatusChanged();
+ }
+}
+
+void Manager::onActiveVoiceCallStatusChanged()
+{
+ VoiceCallHandler* handler = voice->activeVoiceCall();
+ if (!handler) {
+ qCDebug(l) << "ActiveVoiceCall destroyed";
+ watch->endPhoneCall();
+ return;
+ }
+
+ qCDebug(l) << "handlerId:" << handler->handlerId()
+ << "providerId:" << handler->providerId()
+ << "status:" << handler->status()
+ << "statusText:" << handler->statusText()
+ << "lineId:" << handler->lineId()
+ << "incoming:" << handler->isIncoming();
+
+ if (!watch->isConnected()) {
+ qCDebug(l) << "Watch is not connected";
+ return;
+ }
+
+ switch ((VoiceCallHandler::VoiceCallStatus)handler->status()) {
+ case VoiceCallHandler::STATUS_ALERTING:
+ case VoiceCallHandler::STATUS_DIALING:
+ qCDebug(l) << "Tell outgoing:" << handler->lineId();
+ watch->ring(handler->lineId(), findPersonByNumber(handler->lineId()), false);
+ break;
+ case VoiceCallHandler::STATUS_INCOMING:
+ case VoiceCallHandler::STATUS_WAITING:
+ qCDebug(l) << "Tell incoming:" << handler->lineId();
+ watch->ring(handler->lineId(), findPersonByNumber(handler->lineId()));
+ break;
+ case VoiceCallHandler::STATUS_NULL:
+ case VoiceCallHandler::STATUS_DISCONNECTED:
+ qCDebug(l) << "Endphone";
+ watch->endPhoneCall();
+ break;
+ case VoiceCallHandler::STATUS_ACTIVE:
+ qCDebug(l) << "Startphone";
+ watch->startPhoneCall();
+ break;
+ case VoiceCallHandler::STATUS_HELD:
+ break;
+ }
+}
+
+QString Manager::findPersonByNumber(QString number)
+{
+ QString person;
+ numberFilter.setValue(number);
+
+ const QList<QContact> &found = contacts->contacts(numberFilter);
+ if (found.size() == 1) {
+ person = found[0].detail(QContactDetail::TypeDisplayLabel).value(0).toString();
+ }
+
+ if (settings->property("transliterateMessage").toBool()) {
+ transliterateMessage(person);
+ }
+ return person;
+}
+
+void Manager::onVoiceError(const QString &message)
+{
+ qCCritical(l) << "Error:" << message;
+}
+
+
+void Manager::onNotifyError(const QString &message)
+{
+ qWarning() << "Error:" << message;
+}
+
+void Manager::onSmsNotify(const QString &sender, const QString &data)
+{
+ if (settings->property("transliterateMessage").toBool()) {
+ transliterateMessage(sender);
+ transliterateMessage(data);
+ }
+ watch->sendSMSNotification(sender, data);
+}
+
+void Manager::onTwitterNotify(const QString &sender, const QString &data)
+{
+ if (settings->property("transliterateMessage").toBool()) {
+ transliterateMessage(sender);
+ transliterateMessage(data);
+ }
+ watch->sendTwitterNotification(sender, data);
+}
+
+
+void Manager::onFacebookNotify(const QString &sender, const QString &data)
+{
+ if (settings->property("transliterateMessage").toBool()) {
+ transliterateMessage(sender);
+ transliterateMessage(data);
+ }
+ watch->sendFacebookNotification(sender, data);
+}
+
+void Manager::onTelegramNotify(const QString &sender, const QString &data)
+{
+ if (settings->property("transliterateMessage").toBool()) {
+ transliterateMessage(sender);
+ transliterateMessage(data);
+ }
+ watch->sendTelegramNotification(sender, data);
+}
+
+void Manager::onHangoutsNotify(const QString &sender, const QString &data)
+{
+ if (settings->property("transliterateMessage").toBool()) {
+ transliterateMessage(sender);
+ transliterateMessage(data);
+ }
+ watch->sendHangoutsNotification(sender, data);
+}
+
+void Manager::onWhatsappNotify(const QString &sender, const QString &data)
+{
+ if (settings->property("transliterateMessage").toBool()) {
+ transliterateMessage(sender);
+ transliterateMessage(data);
+ }
+ watch->sendWhatsappNotification(sender, data);
+}
+
+void Manager::onEmailNotify(const QString &sender, const QString &data,const QString &subject)
+{
+ if (settings->property("transliterateMessage").toBool()) {
+ transliterateMessage(sender);
+ transliterateMessage(data);
+ transliterateMessage(subject);
+ }
+ watch->sendEmailNotification(sender, data, subject);
+}
+
+void Manager::applyProfile()
+{
+ QString newProfile = settings->property(
+ watch->isConnected() ? "profileWhenConnected"
+ : "profileWhenDisconnected").toString();
+ if (!newProfile.isEmpty()) {
+ QDBusReply<bool> res = QDBusConnection::sessionBus().call(
+ QDBusMessage::createMethodCall("com.nokia.profiled", "/com/nokia/profiled", "com.nokia.profiled", "set_profile")
+ << newProfile);
+ if (res.isValid()) {
+ if (!res.value()) {
+ qCCritical(l) << "Unable to set profile" << newProfile;
+ }
+ }
+ else {
+ qCCritical(l) << res.error().message();
+ }
+ }
+}
+
+void Manager::ping(uint val)
+{
+ qCDebug(l) << "ping" << val;
+
+ if (settings->property("debug").toBool()) {
+ // magic here!
+ // I do not want to add specific debugging methods to pebbled
+ // so just provide some magic Ping() method handling here :-)
+ switch (val) {
+ case 128:
+ watch->sendSMSNotification("SMS", "lorem ipsum");
+ return;
+ case 129:
+ watch->sendEmailNotification("e-mail", "lorem ipsum", "subject");
+ return;
+ case 130:
+ watch->sendFacebookNotification("Facebook", "lorem ipsum");
+ return;
+ case 131:
+ watch->sendTwitterNotification("Twitter", "lorem ipsum");
+ return;
+ case 132:
+ watch->sendMusicNowPlaying("artist", "album", "track name");
+ return;
+ }
+ }
+
+ watch->ping(val);
+}
+
+void Manager::transliterateMessage(const QString &text)
+{
+ if (transliterator.isNull()) {
+ UErrorCode status = U_ZERO_ERROR;
+ transliterator.reset(icu::Transliterator::createInstance(icu::UnicodeString::fromUTF8("Any-Latin; Latin-ASCII"),UTRANS_FORWARD, status));
+ if (U_FAILURE(status)) {
+ qCWarning(l) << "Error creaing ICU Transliterator \"Any-Latin; Latin-ASCII\":" << u_errorName(status);
+ }
+ }
+ if (!transliterator.isNull()) {
+ qCDebug(l) << "String before transliteration:" << text;
+
+ icu::UnicodeString uword = icu::UnicodeString::fromUTF8(text.toStdString());
+ transliterator->transliterate(uword);
+
+ std::string translited;
+ uword.toUTF8String(translited);
+
+ const_cast<QString&>(text) = QString::fromStdString(translited);
+ qCDebug(l) << "String after transliteration:" << text;
+ }
+}
+
+void Manager::onAppNotification(const QUuid &uuid, const QString &title, const QString &body)
+{
+ Q_UNUSED(uuid);
+ watch->sendSMSNotification(title, body);
+}
+
+void Manager::onAppOpened(const QUuid &uuid)
+{
+ currentAppUuid = uuid;
+ emit proxy->AppUuidChanged();
+ emit proxy->AppOpened(uuid.toString());
+}
+
+void Manager::onAppClosed(const QUuid &uuid)
+{
+ currentAppUuid = QUuid();
+ emit proxy->AppClosed(uuid.toString());
+ emit proxy->AppUuidChanged();
+}
+
+bool Manager::uploadFirmware(bool recovery, const QString &file)
+{
+ qCDebug(l) << "about to upload" << (recovery ? "recovery" : "normal")
+ << "firmware:" << file;
+
+ Bundle bundle(Bundle::fromPath(file));
+ if (!bundle.isValid()) {
+ qCWarning(l) << file << "is invalid bundle";
+ return false;
+ }
+
+ if (!bundle.fileExists(Bundle::FIRMWARE) || !bundle.fileExists(Bundle::RESOURCES)) {
+ qCWarning(l) << file << "is missing binary or resource";
+ return false;
+ }
+
+ watch->systemMessage(WatchConnector::systemFIRMWARE_START,
+ [this, recovery, bundle](const QByteArray &data) {
+ qCDebug(l) << "got response to firmware start" << data.toHex();
+
+ QSharedPointer<QIODevice> resourceFile(bundle.openFile(Bundle::RESOURCES));
+ if (!resourceFile) {
+ qCWarning(l) << "failed to open" << bundle.path() << "resource";
+ watch->systemMessage(WatchConnector::systemFIRMWARE_FAIL);
+ return true;
+ }
+
+ upload->uploadFirmwareResources(resourceFile.data(), bundle.crcFile(Bundle::RESOURCES),
+ [this, recovery, bundle, resourceFile]() {
+ qCDebug(l) << "firmware resource upload succesful";
+ resourceFile->close();
+ // Proceed to upload the resource file
+ QSharedPointer<QIODevice> binaryFile(bundle.openFile(Bundle::FIRMWARE));
+ if (binaryFile) {
+ upload->uploadFirmwareBinary(recovery, binaryFile.data(), bundle.crcFile(Bundle::FIRMWARE),
+ [this, binaryFile]() {
+ binaryFile->close();
+ qCDebug(l) << "firmware binary upload succesful";
+ // Upload succesful
+ watch->systemMessage(WatchConnector::systemFIRMWARE_COMPLETE);
+ }, [this, binaryFile](int code) {
+ binaryFile->close();
+ qCWarning(l) << "firmware binary upload failed" << code;
+ watch->systemMessage(WatchConnector::systemFIRMWARE_FAIL);
+ });
+ } else {
+ qCWarning(l) << "failed to open" << bundle.path() << "binary";
+ watch->systemMessage(WatchConnector::systemFIRMWARE_FAIL);
+ }
+ }, [this, resourceFile](int code) {
+ resourceFile->close();
+ qCWarning(l) << "firmware resource upload failed" << code;
+ watch->systemMessage(WatchConnector::systemFIRMWARE_FAIL);
+ });
+
+ return true;
+ });
+
+ return true;
+}
+
+QStringList PebbledProxy::AppSlots() const
+{
+ const int num_slots = manager()->bank->numSlots();
+ QStringList l;
+ l.reserve(num_slots);
+
+ for (int i = 0; i < num_slots; ++i) {
+ if (manager()->bank->isUsed(i)) {
+ QUuid uuid = manager()->bank->appAt(i);
+ l.append(uuid.toString());
+ } else {
+ l.append(QString());
+ }
+ }
+
+ Q_ASSERT(l.size() == num_slots);
+
+ return l;
+}
+
+QVariantList PebbledProxy::AllApps() const
+{
+ QList<QUuid> uuids = manager()->apps->appUuids();
+ QVariantList l;
+
+ foreach (const QUuid &uuid, uuids) {
+ AppInfo info = manager()->apps->info(uuid);
+ QVariantMap m;
+ m.insert("local", QVariant::fromValue(info.isLocal()));
+ m.insert("uuid", QVariant::fromValue(info.uuid().toString()));
+ m.insert("short-name", QVariant::fromValue(info.shortName()));
+ m.insert("long-name", QVariant::fromValue(info.longName()));
+ m.insert("company-name", QVariant::fromValue(info.companyName()));
+ m.insert("version-label", QVariant::fromValue(info.versionLabel()));
+ m.insert("is-watchface", QVariant::fromValue(info.isWatchface()));
+ m.insert("configurable", QVariant::fromValue(info.capabilities().testFlag(AppInfo::Capability::Configurable)));
+ m.insert("path", QVariant::fromValue(info.path()));
+
+ if (!info.getMenuIconImage().isNull()) {
+ m.insert("menu-icon", QVariant::fromValue(info.getMenuIconPng()));
+ }
+
+ l.append(QVariant::fromValue(m));
+ }
+
+ return l;
+}
+
+bool PebbledProxy::SendAppMessage(const QString &uuid, const QVariantMap &data)
+{
+ Q_ASSERT(calledFromDBus());
+ const QDBusMessage msg = message();
+ setDelayedReply(true);
+ manager()->appmsg->send(uuid, data, [this, msg]() {
+ QDBusMessage reply = msg.createReply(QVariant::fromValue(true));
+ this->connection().send(reply);
+ }, [this, msg]() {
+ QDBusMessage reply = msg.createReply(QVariant::fromValue(false));
+ this->connection().send(reply);
+ });
+ return false; // D-Bus clients should never see this reply.
+}
+
+QString PebbledProxy::StartAppConfiguration(const QString &uuid)
+{
+ Q_ASSERT(calledFromDBus());
+ const QDBusMessage msg = message();
+ QDBusConnection conn = connection();
+
+ if (manager()->currentAppUuid != uuid) {
+ qCWarning(l) << "Called StartAppConfiguration but the uuid" << uuid << "is not running";
+ sendErrorReply(msg.interface() + ".Error.AppNotRunning",
+ "The requested app is not currently opened in the watch");
+ return QString();
+ }
+
+ if (!manager()->js->isJSKitAppRunning()) {
+ qCWarning(l) << "Called StartAppConfiguration but the uuid" << uuid << "is not a JS app";
+ sendErrorReply(msg.interface() + ".Error.JSNotActive",
+ "The requested app is not a PebbleKit JS application");
+ return QString();
+ }
+
+ // After calling showConfiguration() on the script,
+ // it will (eventually!) return a URL to us via the appOpenUrl signal.
+
+ // So we can't send the D-Bus reply right now.
+ setDelayedReply(true);
+
+ // Set up a signal handler to catch the appOpenUrl signal.
+ QMetaObject::Connection *c = new QMetaObject::Connection;
+ *c = connect(manager()->js, &JSKitManager::appOpenUrl,
+ this, [this,conn,msg,c](const QUrl &url) {
+ // Workaround: due to a GCC crash we can't capture the uuid parameter, but we can extract
+ // it again from the original message arguments.
+ // Suspect GCC bug# is 59195, 61233, or 61321.
+ // TODO Possibly fixed in 4.9.0
+ const QString uuid = msg.arguments().at(0).toString();
+
+ if (manager()->currentAppUuid != uuid) {
+ // App was changed while we were waiting for the script..
+ QDBusMessage reply = msg.createErrorReply(msg.interface() + ".Error.AppNotRunning",
+ "The requested app is not currently opened in the watch");
+ conn.send(reply);
+ } else {
+ QDBusMessage reply = msg.createReply(QVariant::fromValue(url.toString()));
+ conn.send(reply);
+ }
+
+ disconnect(*c);
+ delete c;
+ });
+
+ // TODO: JS script may fail, never call OpenURL, or something like that
+ // In those cases we WILL leak the above connection.
+ // (at least until the next appOpenURL event comes in)
+ // So we need to also set a timeout or similar.
+
+ manager()->js->showConfiguration();
+
+ // Note that the above signal handler _might_ have been already called by this point.
+
+ return QString(); // This return value should never be used.
+}
+
+void PebbledProxy::SendAppConfigurationData(const QString &uuid, const QString &data)
+{
+ Q_ASSERT(calledFromDBus());
+ const QDBusMessage msg = message();
+
+ if (manager()->currentAppUuid != uuid) {
+ sendErrorReply(msg.interface() + ".Error.AppNotRunning",
+ "The requested app is not currently opened in the watch");
+ return;
+ }
+
+ if (!manager()->js->isJSKitAppRunning()) {
+ sendErrorReply(msg.interface() + ".Error.JSNotActive",
+ "The requested app is not a PebbleKit JS application");
+ return;
+ }
+
+ manager()->js->handleWebviewClosed(data);
+}
+
+void PebbledProxy::UnloadApp(int slot)
+{
+ Q_ASSERT(calledFromDBus());
+ const QDBusMessage msg = message();
+
+ if (!manager()->bank->unloadApp(slot)) {
+ sendErrorReply(msg.interface() + ".Error.CannotUnload",
+ "Cannot unload application");
+ }
+}
+
+void PebbledProxy::UploadApp(const QString &uuid, int slot)
+{
+ Q_ASSERT(calledFromDBus());
+ const QDBusMessage msg = message();
+
+ if (!manager()->bank->uploadApp(QUuid(uuid), slot)) {
+ sendErrorReply(msg.interface() + ".Error.CannotUpload",
+ "Cannot upload application");
+ }
+}
+
+void PebbledProxy::NotifyFirmware(bool ok)
+{
+ Q_ASSERT(calledFromDBus());
+ manager()->watch->sendFirmwareState(ok);
+}
+
+void PebbledProxy::UploadFirmware(bool recovery, const QString &file)
+{
+ Q_ASSERT(calledFromDBus());
+ const QDBusMessage msg = message();
+
+ if (!manager()->uploadFirmware(recovery, file)) {
+ sendErrorReply(msg.interface() + ".Error.CannotUpload",
+ "Cannot upload firmware");
+ }
+}
diff --git a/daemon/manager.h b/daemon/manager.h index e3143b1..0c6032b 100644 --- a/daemon/manager.h +++ b/daemon/manager.h @@ -1,157 +1,160 @@ -#ifndef MANAGER_H -#define MANAGER_H - -#include "watchconnector.h" -#include "uploadmanager.h" -#include "voicecallmanager.h" -#include "notificationmanager.h" -#include "musicmanager.h" -#include "datalogmanager.h" -#include "appmsgmanager.h" -#include "jskitmanager.h" -#include "appmanager.h" -#include "bankmanager.h" -#include "settings.h" - -#include <QObject> -#include <QDBusContext> -#include <QtContacts/QContactManager> -#include <QtContacts/QContactDetailFilter> -#include <MNotification> -#include <QLoggingCategory> - -#include <unicode/translit.h> - -using namespace QtContacts; - -class PebbledProxy; - -class Manager : public QObject, protected QDBusContext -{ - Q_OBJECT - QLoggingCategory l; - - friend class PebbledProxy; - - Settings *settings; - - PebbledProxy *proxy; - - WatchConnector *watch; - UploadManager *upload; - AppManager *apps; - BankManager *bank; - VoiceCallManager *voice; - NotificationManager *notifications; - MusicManager *music; - DataLogManager *datalog; - AppMsgManager *appmsg; - JSKitManager *js; - - MNotification notification; - - QContactManager *contacts; - QContactDetailFilter numberFilter; - - QUuid currentAppUuid; - - QScopedPointer<icu::Transliterator> transliterator; - -public: - explicit Manager(Settings *settings, QObject *parent = 0); - ~Manager(); - - QString findPersonByNumber(QString number); - - bool uploadFirmware(bool recovery, const QString &file); - -protected: - void transliterateMessage(const QString &text); - -public slots: - void applyProfile(); - void ping(uint val); - -private slots: - void onSettingChanged(const QString &key); - void onSettingsChanged(); - void onConnectedChanged(); - void onActiveVoiceCallChanged(); - void onVoiceError(const QString &message); - void onActiveVoiceCallStatusChanged(); - void onNotifyError(const QString &message); - void onSmsNotify(const QString &sender, const QString &data); - void onTwitterNotify(const QString &sender, const QString &data); - void onFacebookNotify(const QString &sender, const QString &data); - void onEmailNotify(const QString &sender, const QString &data,const QString &subject); - - void onAppNotification(const QUuid &uuid, const QString &title, const QString &body); - void onAppOpened(const QUuid &uuid); - void onAppClosed(const QUuid &uuid); -}; - -/** This class is what's actually exported over D-Bus, - * so the names of the slots and properties must match with org.pebbled.Watch D-Bus interface. - * Case sensitive. Otherwise, _runtime_ failures will occur. */ -// Some of the methods are marked inline so that they may be inlined inside qt_metacall -class PebbledProxy : public QObject, protected QDBusContext -{ - Q_OBJECT - QLoggingCategory l; - - Q_PROPERTY(QString Name READ Name NOTIFY NameChanged) - Q_PROPERTY(QString Address READ Address NOTIFY AddressChanged) - Q_PROPERTY(QVariantMap Info READ Info NOTIFY InfoChanged) - Q_PROPERTY(bool Connected READ Connected NOTIFY ConnectedChanged) - Q_PROPERTY(QString AppUuid READ AppUuid NOTIFY AppUuidChanged) - Q_PROPERTY(QStringList AppSlots READ AppSlots NOTIFY AppSlotsChanged) - Q_PROPERTY(QVariantList AllApps READ AllApps NOTIFY AllAppsChanged) - - inline Manager* manager() const { return static_cast<Manager*>(parent()); } - -public: - inline explicit PebbledProxy(QObject *parent) - : QObject(parent), l(metaObject()->className()) {} - - inline QString Name() const { qCDebug(l) << manager()->watch->name(); return manager()->watch->name(); } - inline QString Address() const { qCDebug(l) << manager()->watch->address().toString(); return manager()->watch->address().toString(); } - inline QVariantMap Info() const { qCDebug(l) << manager()->watch->versions().toMap(); return manager()->watch->versions().toMap(); } - inline bool Connected() const { qCDebug(l) << manager()->watch->isConnected(); return manager()->watch->isConnected(); } - inline QString AppUuid() const { return manager()->currentAppUuid.toString(); } - - QStringList AppSlots() const; - - QVariantList AllApps() const; - -public slots: - inline void Disconnect() { manager()->watch->disconnect(); } - inline void Reconnect() { manager()->watch->connect(); } - inline void Ping(uint val) { manager()->ping(val); } - inline void SyncTime() { manager()->watch->time(); } - - inline void LaunchApp(const QString &uuid) { manager()->appmsg->launchApp(uuid); } - inline void CloseApp(const QString &uuid) { manager()->appmsg->closeApp(uuid); } - - bool SendAppMessage(const QString &uuid, const QVariantMap &data); - QString StartAppConfiguration(const QString &uuid); - void SendAppConfigurationData(const QString &uuid, const QString &data); - - void UnloadApp(int slot); - void UploadApp(const QString &uuid, int slot); - - void NotifyFirmware(bool ok); - void UploadFirmware(bool recovery, const QString &file); - -signals: - void NameChanged(); - void AddressChanged(); - void InfoChanged(); - void ConnectedChanged(); - void AppUuidChanged(); - void AppSlotsChanged(); - void AllAppsChanged(); - void AppOpened(const QString &uuid); - void AppClosed(const QString &uuid); -}; - -#endif // MANAGER_H +#ifndef MANAGER_H
+#define MANAGER_H
+
+#include "watchconnector.h"
+#include "uploadmanager.h"
+#include "voicecallmanager.h"
+#include "notificationmanager.h"
+#include "musicmanager.h"
+#include "datalogmanager.h"
+#include "appmsgmanager.h"
+#include "jskitmanager.h"
+#include "appmanager.h"
+#include "bankmanager.h"
+#include "settings.h"
+
+#include <QObject>
+#include <QDBusContext>
+#include <QtContacts/QContactManager>
+#include <QtContacts/QContactDetailFilter>
+#include <MNotification>
+#include <QLoggingCategory>
+
+#include <unicode/translit.h>
+
+using namespace QtContacts;
+
+class PebbledProxy;
+
+class Manager : public QObject, protected QDBusContext
+{
+ Q_OBJECT
+ QLoggingCategory l;
+
+ friend class PebbledProxy;
+
+ Settings *settings;
+
+ PebbledProxy *proxy;
+
+ WatchConnector *watch;
+ UploadManager *upload;
+ AppManager *apps;
+ BankManager *bank;
+ VoiceCallManager *voice;
+ NotificationManager *notifications;
+ MusicManager *music;
+ DataLogManager *datalog;
+ AppMsgManager *appmsg;
+ JSKitManager *js;
+
+ MNotification notification;
+
+ QContactManager *contacts;
+ QContactDetailFilter numberFilter;
+
+ QUuid currentAppUuid;
+
+ QScopedPointer<icu::Transliterator> transliterator;
+
+public:
+ explicit Manager(Settings *settings, QObject *parent = 0);
+ ~Manager();
+
+ QString findPersonByNumber(QString number);
+
+ bool uploadFirmware(bool recovery, const QString &file);
+
+protected:
+ void transliterateMessage(const QString &text);
+
+public slots:
+ void applyProfile();
+ void ping(uint val);
+
+private slots:
+ void onSettingChanged(const QString &key);
+ void onSettingsChanged();
+ void onConnectedChanged();
+ void onActiveVoiceCallChanged();
+ void onVoiceError(const QString &message);
+ void onActiveVoiceCallStatusChanged();
+ void onNotifyError(const QString &message);
+ void onSmsNotify(const QString &sender, const QString &data);
+ void onTwitterNotify(const QString &sender, const QString &data);
+ void onFacebookNotify(const QString &sender, const QString &data);
+ void onTelegramNotify(const QString &sender, const QString &data);
+ void onHangoutsNotify(const QString &sender, const QString &data);
+ void onWhatsappNotify(const QString &sender, const QString &data);
+ void onEmailNotify(const QString &sender, const QString &data,const QString &subject);
+
+ void onAppNotification(const QUuid &uuid, const QString &title, const QString &body);
+ void onAppOpened(const QUuid &uuid);
+ void onAppClosed(const QUuid &uuid);
+};
+
+/** This class is what's actually exported over D-Bus,
+ * so the names of the slots and properties must match with org.pebbled.Watch D-Bus interface.
+ * Case sensitive. Otherwise, _runtime_ failures will occur. */
+// Some of the methods are marked inline so that they may be inlined inside qt_metacall
+class PebbledProxy : public QObject, protected QDBusContext
+{
+ Q_OBJECT
+ QLoggingCategory l;
+
+ Q_PROPERTY(QString Name READ Name NOTIFY NameChanged)
+ Q_PROPERTY(QString Address READ Address NOTIFY AddressChanged)
+ Q_PROPERTY(QVariantMap Info READ Info NOTIFY InfoChanged)
+ Q_PROPERTY(bool Connected READ Connected NOTIFY ConnectedChanged)
+ Q_PROPERTY(QString AppUuid READ AppUuid NOTIFY AppUuidChanged)
+ Q_PROPERTY(QStringList AppSlots READ AppSlots NOTIFY AppSlotsChanged)
+ Q_PROPERTY(QVariantList AllApps READ AllApps NOTIFY AllAppsChanged)
+
+ inline Manager* manager() const { return static_cast<Manager*>(parent()); }
+
+public:
+ inline explicit PebbledProxy(QObject *parent)
+ : QObject(parent), l(metaObject()->className()) {}
+
+ inline QString Name() const { qCDebug(l) << manager()->watch->name(); return manager()->watch->name(); }
+ inline QString Address() const { qCDebug(l) << manager()->watch->address().toString(); return manager()->watch->address().toString(); }
+ inline QVariantMap Info() const { qCDebug(l) << manager()->watch->versions().toMap(); return manager()->watch->versions().toMap(); }
+ inline bool Connected() const { qCDebug(l) << manager()->watch->isConnected(); return manager()->watch->isConnected(); }
+ inline QString AppUuid() const { return manager()->currentAppUuid.toString(); }
+
+ QStringList AppSlots() const;
+
+ QVariantList AllApps() const;
+
+public slots:
+ inline void Disconnect() { manager()->watch->disconnect(); }
+ inline void Reconnect() { manager()->watch->connect(); }
+ inline void Ping(uint val) { manager()->ping(val); }
+ inline void SyncTime() { manager()->watch->time(); }
+
+ inline void LaunchApp(const QString &uuid) { manager()->appmsg->launchApp(uuid); }
+ inline void CloseApp(const QString &uuid) { manager()->appmsg->closeApp(uuid); }
+
+ bool SendAppMessage(const QString &uuid, const QVariantMap &data);
+ QString StartAppConfiguration(const QString &uuid);
+ void SendAppConfigurationData(const QString &uuid, const QString &data);
+
+ void UnloadApp(int slot);
+ void UploadApp(const QString &uuid, int slot);
+
+ void NotifyFirmware(bool ok);
+ void UploadFirmware(bool recovery, const QString &file);
+
+signals:
+ void NameChanged();
+ void AddressChanged();
+ void InfoChanged();
+ void ConnectedChanged();
+ void AppUuidChanged();
+ void AppSlotsChanged();
+ void AllAppsChanged();
+ void AppOpened(const QString &uuid);
+ void AppClosed(const QString &uuid);
+};
+
+#endif // MANAGER_H
diff --git a/daemon/notificationmanager.cpp b/daemon/notificationmanager.cpp index 8e21ad9..0c56bc2 100644 --- a/daemon/notificationmanager.cpp +++ b/daemon/notificationmanager.cpp @@ -114,7 +114,7 @@ uint NotificationManager::Notify(const QString &app_name, uint replaces_id, cons Q_UNUSED(expire_timeout); // new place to check notification owner in Sailfish 1.1.6 - QString owner = hints.value("x-nemo-owner", "none").toString(); + QString owner = hints.value("x-nemo-owner", "Unknown").toString(); qCDebug(l) << Q_FUNC_INFO << "Got notification via dbus from" << this->getCleanAppName(app_name) << " Owner: " << owner; qCDebug(l) << hints; @@ -127,79 +127,81 @@ uint NotificationManager::Notify(const QString &app_name, uint replaces_id, cons QString category = hints.value("category", "").toString(); QStringHash categoryParams = this->getCategoryParams(category); - // Ignore transient notifications (notif hints override category hints) - // Hack this to accept transient -preview and -summary notifications, as we don't know how to decode the actual notifs yet - if (!category.endsWith("preview") && !category.endsWith("summary") && - hints.value("transient", categoryParams.value("transient", "false")).toString() == "true") { - qCDebug(l) << "Ignoring transient notification from " << owner; - return 0; - } - - if (app_name == "messageserver5" || owner == "messageserver5") { + if (category == "x-nemo.email") { if (!settings->property("notificationsEmails").toBool()) { qCDebug(l) << "Ignoring email notification because of setting!"; return 0; } - -// This is how we should obtain messaging notifications, if we manage to deserialize this 'PersonalNotification'. -// QByteArray commdata = hints.value("x-commhistoryd-data", "no data").toByteArray(); -// if (!commdata.contains((char)0)) -// data = QByteArray::fromBase64(commdata); -// QDataStream stream(commdata); -// stream.setVersion(QDataStream::Qt_5_0); -// stream >> - - QString subject = hints.value("x-nemo-preview-summary", "").toString(); - QString data = hints.value("x-nemo-preview-body", "").toString(); - - // Prioritize subject over data - if (subject.isEmpty() && !data.isEmpty()) { - subject = data; - data = ""; - } - - if (!subject.isEmpty()) { - emit this->emailNotify(subject, data, ""); - } - } else if (app_name == "commhistoryd" || owner == "commhistoryd") { - if (summary == "" && body == "") { - if (category == "x-nemo.call.missed") { - if (!settings->property("notificationsMissedCall").toBool()) { - qCDebug(l) << "Ignoring MissedCall notification because of setting!"; - return 0; - } - } else { - if (!settings->property("notificationsCommhistoryd").toBool()) { - qCDebug(l) << "Ignoring commhistoryd notification because of setting!"; - return 0; - } - } - emit this->smsNotify(hints.value("x-nemo-preview-summary", "default").toString(), - hints.value("x-nemo-preview-body", "default").toString() - ); - } - } else if (app_name == "harbour-mitakuuluu2-server" || owner == "harbour-mitakuuluu2-server") { - if (!settings->property("notificationsMitakuuluu").toBool()) { - qCDebug(l) << "Ignoring mitakuuluu notification because of setting!"; + emit this->emailNotify(summary, body, app_name); + } + else if (category == "x-nemo.call.missed") { + if (!settings->property("notificationsMissedCall").toBool()) { + qCDebug(l) << "Ignoring MissedCall notification because of setting!"; return 0; } - - emit this->smsNotify(hints.value("x-nemo-preview-body", "default").toString(), - hints.value("x-nemo-preview-summary", "default").toString() + emit this->smsNotify(hints.value("x-nemo-preview-summary", summary).toString(), + hints.value("x-nemo-preview-body", body).toString() + ); + } + else if (category == "x-nemo.messaging.sms.preview") { + if (!settings->property("notificationsCommhistoryd").toBool()) { + qCDebug(l) << "Ignoring commhistoryd notification because of setting!"; + return 0; + } + emit this->smsNotify(hints.value("x-nemo-preview-summary", summary).toString(), + hints.value("x-nemo-preview-body", body).toString() ); - } else if (app_name == "twitter-notifications-client" || owner == "twitter-notifications-client") { + } + else if (app_name.toLower().contains("whatsapp") || app_name.toLower().contains("whatsup")) { + if (!settings->property("notificationsWhatsapp").toBool()) { + qCDebug(l) << "Ignoring whatsapp notification because of setting!"; + return 0; + } + emit this->whatsappNotify(hints.value("x-nemo-preview-summary", summary).toString(), + hints.value("x-nemo-preview-body", body).toString() + ); + } + else if (hints.value("x-nemo-origin-package").toString() == "org.telegram.messenger" + || category.startsWith("harbour.sailorgram")) { + if (!settings->property("notificationsTelegram").toBool()) { + qCDebug(l) << "Ignoring telegram notification because of setting!"; + return 0; + } + emit this->telegramNotify(hints.value("x-nemo-preview-summary", summary).toString(), + hints.value("x-nemo-preview-body", body).toString() + ); + } + else if (app_name.toLower().contains("hangouts") || app_name.toLower().contains("hangish")) { + if (!settings->property("notificationsHangouts").toBool()) { + qCDebug(l) << "Ignoring telegram notification because of setting!"; + return 0; + } + emit this->hangoutNotify(hints.value("x-nemo-preview-summary", summary).toString(), + hints.value("x-nemo-preview-body", body).toString() + ); + } + else if (app_name == "twitter-notifications-client" || owner == "twitter-notifications-client") { if (!settings->property("notificationsTwitter").toBool()) { qCDebug(l) << "Ignoring twitter notification because of setting!"; return 0; } - emit this->twitterNotify(hints.value("x-nemo-preview-body", body).toString(), - hints.value("x-nemo-preview-summary", summary).toString() + emit this->twitterNotify(hints.value("x-nemo-preview-summary", summary).toString(), + hints.value("x-nemo-preview-body", body).toString() ); } else { + + // Ignore transient notifications (notif hints override category hints) + // Hack this to accept transient -preview and -summary notifications, as we don't know how to decode the actual notifs yet + if (!category.endsWith("preview") && !category.endsWith("summary") && + hints.value("transient", categoryParams.value("transient", "false")).toString() == "true") { + qCDebug(l) << "Ignoring transient notification from " << owner; + return 0; + } + // Prioritize x-nemo-preview* over dbus direct summary and body - QString subject = hints.value("x-nemo-preview-summary", "").toString(); - QString data = hints.value("x-nemo-preview-body", "").toString(); + QString subject = hints.value("x-nemo-preview-summary", summary).toString(); + QString data = hints.value("x-nemo-preview-body", body).toString(); int prio = categoryParams.value("x-nemo-priority", "0").toInt(); qCDebug(l) << "MSG Prio:" << prio; diff --git a/daemon/notificationmanager.h b/daemon/notificationmanager.h index 037ff07..830bfb4 100644 --- a/daemon/notificationmanager.h +++ b/daemon/notificationmanager.h @@ -29,6 +29,9 @@ Q_SIGNALS: void smsNotify(const QString &sender, const QString &data); void twitterNotify(const QString &sender, const QString &data); void facebookNotify(const QString &sender, const QString &data); + void telegramNotify(const QString &sender, const QString &data); + void hangoutNotify(const QString &sender, const QString &data); + void whatsappNotify(const QString &sender, const QString &data); void emailNotify(const QString &sender, const QString &data,const QString &subject); public Q_SLOTS: diff --git a/daemon/settings.h b/daemon/settings.h index 4534a54..826f0ce 100644 --- a/daemon/settings.h +++ b/daemon/settings.h @@ -1,81 +1,89 @@ -#ifndef SETTINGS_H -#define SETTINGS_H - -#include <MDConfGroup> - -class Settings : public MDConfGroup -{ - Q_OBJECT - - Q_PROPERTY(QString profileWhenConnected MEMBER profileWhenConnected NOTIFY profileWhenConnectedChanged) - Q_PROPERTY(QString profileWhenDisconnected MEMBER profileWhenDisconnected NOTIFY profileWhenDisconnectedChanged) - Q_PROPERTY(bool transliterateMessage MEMBER transliterateMessage NOTIFY transliterateMessageChanged) - Q_PROPERTY(bool useSystemVolume MEMBER useSystemVolume NOTIFY useSystemVolumeChanged) - Q_PROPERTY(bool incomingCallNotification MEMBER incomingCallNotification NOTIFY incomingCallNotificationChanged) - Q_PROPERTY(bool notificationsCommhistoryd MEMBER notificationsCommhistoryd NOTIFY notificationsCommhistorydChanged) - Q_PROPERTY(bool notificationsMissedCall MEMBER notificationsMissedCall NOTIFY notificationsMissedCallChanged) - Q_PROPERTY(bool notificationsEmails MEMBER notificationsEmails NOTIFY notificationsEmailsChanged) - Q_PROPERTY(bool notificationsMitakuuluu MEMBER notificationsMitakuuluu NOTIFY notificationsMitakuuluuChanged) - Q_PROPERTY(bool notificationsTwitter MEMBER notificationsTwitter NOTIFY notificationsTwitterChanged) - Q_PROPERTY(bool notificationsFacebook MEMBER notificationsFacebook NOTIFY notificationsFacebookChanged) - Q_PROPERTY(bool notificationsOther MEMBER notificationsOther NOTIFY notificationsOtherChanged) - Q_PROPERTY(bool notificationsAll MEMBER notificationsAll NOTIFY notificationsAllChanged) - Q_PROPERTY(QString accountToken MEMBER accountToken NOTIFY accountTokenChanged) - Q_PROPERTY(bool debug MEMBER debug NOTIFY debugChanged) - - QString profileWhenConnected; - QString profileWhenDisconnected; - bool transliterateMessage; - bool useSystemVolume; - bool incomingCallNotification; - bool notificationsCommhistoryd; - bool notificationsMissedCall; - bool notificationsEmails; - bool notificationsMitakuuluu; - bool notificationsTwitter; - bool notificationsFacebook; - bool notificationsOther; - bool notificationsAll; - QString accountToken; - bool debug; - -public: - explicit Settings(QObject *parent = 0) : - MDConfGroup("/org/pebbled/settings", parent, BindProperties), - transliterateMessage(false), - useSystemVolume(true), - incomingCallNotification(true), - notificationsCommhistoryd(true), - notificationsMissedCall(true), - notificationsEmails(false), - notificationsMitakuuluu(true), - notificationsTwitter(true), - notificationsFacebook(true), - notificationsOther(true), - notificationsAll(false), - debug(false) - { - resolveMetaObject(); - QMetaObject::invokeMethod(this, "propertyChanged", Qt::DirectConnection); - sync(); - } - -signals: - void profileWhenConnectedChanged(); - void profileWhenDisconnectedChanged(); - void transliterateMessageChanged(); - void useSystemVolumeChanged(); - void incomingCallNotificationChanged(); - void notificationsCommhistorydChanged(); - void notificationsMissedCallChanged(); - void notificationsEmailsChanged(); - void notificationsMitakuuluuChanged(); - void notificationsTwitterChanged(); - void notificationsFacebookChanged(); - void notificationsOtherChanged(); - void notificationsAllChanged(); - void accountTokenChanged(); - void debugChanged(); -}; - -#endif // SETTINGS_H +#ifndef SETTINGS_H
+#define SETTINGS_H
+
+#include <MDConfGroup>
+
+class Settings : public MDConfGroup
+{
+ Q_OBJECT
+
+ Q_PROPERTY(QString profileWhenConnected MEMBER profileWhenConnected NOTIFY profileWhenConnectedChanged)
+ Q_PROPERTY(QString profileWhenDisconnected MEMBER profileWhenDisconnected NOTIFY profileWhenDisconnectedChanged)
+ Q_PROPERTY(bool transliterateMessage MEMBER transliterateMessage NOTIFY transliterateMessageChanged)
+ Q_PROPERTY(bool useSystemVolume MEMBER useSystemVolume NOTIFY useSystemVolumeChanged)
+ Q_PROPERTY(bool incomingCallNotification MEMBER incomingCallNotification NOTIFY incomingCallNotificationChanged)
+ Q_PROPERTY(bool notificationsCommhistoryd MEMBER notificationsCommhistoryd NOTIFY notificationsCommhistorydChanged)
+ Q_PROPERTY(bool notificationsMissedCall MEMBER notificationsMissedCall NOTIFY notificationsMissedCallChanged)
+ Q_PROPERTY(bool notificationsEmails MEMBER notificationsEmails NOTIFY notificationsEmailsChanged)
+ Q_PROPERTY(bool notificationsTwitter MEMBER notificationsTwitter NOTIFY notificationsTwitterChanged)
+ Q_PROPERTY(bool notificationsFacebook MEMBER notificationsFacebook NOTIFY notificationsFacebookChanged)
+ Q_PROPERTY(bool notificationsTelegram MEMBER notificationsTelegram NOTIFY notificationsTelegramChanged)
+ Q_PROPERTY(bool notificationsWhatsapp MEMBER notificationsWhatsapp NOTIFY notificationsWhatsappChanged)
+ Q_PROPERTY(bool notificationsHangouts MEMBER notificationsHangouts NOTIFY notificationsHangoutsChanged)
+ Q_PROPERTY(bool notificationsOther MEMBER notificationsOther NOTIFY notificationsOtherChanged)
+ Q_PROPERTY(bool notificationsAll MEMBER notificationsAll NOTIFY notificationsAllChanged)
+ Q_PROPERTY(QString accountToken MEMBER accountToken NOTIFY accountTokenChanged)
+ Q_PROPERTY(bool debug MEMBER debug NOTIFY debugChanged)
+
+ QString profileWhenConnected;
+ QString profileWhenDisconnected;
+ bool transliterateMessage;
+ bool useSystemVolume;
+ bool incomingCallNotification;
+ bool notificationsCommhistoryd;
+ bool notificationsMissedCall;
+ bool notificationsEmails;
+ bool notificationsTwitter;
+ bool notificationsFacebook;
+ bool notificationsTelegram;
+ bool notificationsWhatsapp;
+ bool notificationsHangouts;
+ bool notificationsOther;
+ bool notificationsAll;
+ QString accountToken;
+ bool debug;
+
+public:
+ explicit Settings(QObject *parent = 0) :
+ MDConfGroup("/org/pebbled/settings", parent, BindProperties),
+ transliterateMessage(false),
+ useSystemVolume(true),
+ incomingCallNotification(true),
+ notificationsCommhistoryd(true),
+ notificationsMissedCall(true),
+ notificationsEmails(false),
+ notificationsTwitter(true),
+ notificationsFacebook(true),
+ notificationsTelegram(true),
+ notificationsWhatsapp(true),
+ notificationsHangouts(true),
+ notificationsOther(true),
+ notificationsAll(false),
+ debug(false)
+ {
+ resolveMetaObject();
+ QMetaObject::invokeMethod(this, "propertyChanged", Qt::DirectConnection);
+ sync();
+ }
+
+signals:
+ void profileWhenConnectedChanged();
+ void profileWhenDisconnectedChanged();
+ void transliterateMessageChanged();
+ void useSystemVolumeChanged();
+ void incomingCallNotificationChanged();
+ void notificationsCommhistorydChanged();
+ void notificationsMissedCallChanged();
+ void notificationsEmailsChanged();
+ void notificationsTwitterChanged();
+ void notificationsFacebookChanged();
+ void notificationsTelegramChanged();
+ void notificationsWhatsappChanged();
+ void notificationsHangoutsChanged();
+ void notificationsOtherChanged();
+ void notificationsAllChanged();
+ void accountTokenChanged();
+ void debugChanged();
+};
+
+#endif // SETTINGS_H
diff --git a/daemon/timelineitem.cpp b/daemon/timelineitem.cpp new file mode 100644 index 0000000..4bc699c --- /dev/null +++ b/daemon/timelineitem.cpp @@ -0,0 +1,144 @@ +#include "timelineitem.h" + +TimelineItem::TimelineItem(TimelineItem::Type type, Flags flags, const QDateTime ×tamp, quint16 duration): + TimelineItem(QUuid::createUuid(), type, flags, timestamp, duration) +{ + +} + +TimelineItem::TimelineItem(const QUuid &uuid, TimelineItem::Type type, Flags flags, const QDateTime ×tamp, quint16 duration): + PebblePacket(), + m_itemId(uuid), + m_timestamp(timestamp), + m_duration(duration), + m_type(type), + m_flags(flags) +{ + +} + +QUuid TimelineItem::itemId() const +{ + return m_itemId; +} + +void TimelineItem::setLayout(quint8 layout) +{ + m_layout = layout; +} + +void TimelineItem::setFlags(Flags flags) +{ + m_flags = flags; +} + +void TimelineItem::appendAttribute(const TimelineAttribute &attribute) +{ + m_attributes.append(attribute); +} + +void TimelineItem::appendAction(const TimelineAction &action) +{ + m_actions.append(action); +} + +QList<TimelineAttribute> TimelineItem::attributes() const +{ + return m_attributes; +} + +QList<TimelineAction> TimelineItem::actions() const +{ + return m_actions; +} + +QByteArray TimelineItem::serialize() const +{ + QByteArray ret; + ret.append(m_itemId.toRfc4122()); + ret.append(m_parentId.toRfc4122()); + int ts = m_timestamp.toMSecsSinceEpoch() / 1000; + ret.append(ts & 0xFF); ret.append((ts >> 8) & 0xFF); ret.append((ts >> 16) & 0xFF); ret.append((ts >> 24) & 0xFF); + ret.append(m_duration & 0xFF); ret.append(((m_duration >> 8) & 0xFF)); + ret.append((quint8)m_type); + ret.append(m_flags & 0xFF); ret.append(((m_flags >> 8) & 0xFF)); + ret.append(m_layout); + + QByteArray serializedAttributes; + foreach (const TimelineAttribute &attribute, m_attributes) { + serializedAttributes.append(attribute.serialize()); + } + + QByteArray serializedActions; + foreach (const TimelineAction &action, m_actions) { + serializedActions.append(action.serialize()); + } + quint16 dataLength = serializedAttributes.length() + serializedActions.length(); + ret.append(dataLength & 0xFF); ret.append(((dataLength >> 8) & 0xFF)); + ret.append(m_attributes.count()); + ret.append(m_actions.count()); + ret.append(serializedAttributes); + ret.append(serializedActions); + return ret; +} + +TimelineAction::TimelineAction(quint8 actionId, TimelineAction::Type type, const QList<TimelineAttribute> &attributes): + PebblePacket(), + m_actionId(actionId), + m_type(type), + m_attributes(attributes) +{ + +} + +void TimelineAction::appendAttribute(const TimelineAttribute &attribute) +{ + m_attributes.append(attribute); +} + +void TimelineAttribute::setContent(const QString &content) +{ + m_content = content.toUtf8(); +} + +void TimelineAttribute::setContent(TimelineAttribute::IconID iconId) +{ + m_content.clear(); + m_content.append((quint8)iconId); + m_content.append('\0'); + m_content.append('\0'); + m_content.append(0x80); +} + +void TimelineAttribute::setContent(TimelineAttribute::Color color) +{ + m_content.clear(); + m_content.append((quint8)color); +} + +void TimelineAttribute::setContent(const QStringList &values) +{ + m_content.clear(); + foreach (const QString &value, values) { + if (!m_content.isEmpty()) { + m_content.append('\0'); + } + m_content.append(value.toUtf8()); + } +} + +void TimelineAttribute::setContent(quint8 data) +{ + m_content.clear(); + m_content.append(data); +} + +QByteArray TimelineAttribute::serialize() const +{ + QByteArray ret; + ret.append((quint8)m_type); + ret.append(m_content.length() & 0xFF); ret.append(((m_content.length() >> 8) & 0xFF)); // length + ret.append(m_content); + return ret; +} + diff --git a/daemon/timelineitem.h b/daemon/timelineitem.h new file mode 100644 index 0000000..9d7850c --- /dev/null +++ b/daemon/timelineitem.h @@ -0,0 +1,194 @@ +#ifndef TIMELINEITEM_H +#define TIMELINEITEM_H + +#include <QByteArray> +#include <QDateTime> + +#include "watchconnector.h" + + +class TimelineAttribute +{ +public: + enum Type { + TypeTitle = 0x01, + TypeSubtitle = 0x02, + TypeBody = 0x03, + TypeTinyIcon = 0x04, + TypeLargeIcon = 0x06, + TypeFieldNames = 0x19, + TypeFieldValues = 0x1a, + TypeColor = 0x1c, + TypeRecurring = 0x1f + }; + enum IconID { + IconIDDefaultBell = 0x01, + IconIDDefaultMissedCall = 0x02, + IconIDReminder = 0x03, + IconIDFlag = 0x04, + IconIDWhatsApp = 0x05, + IconIDTwitter = 0x06, + IconIDTelegram = 0x07, + IconIDHangout = 0x08, + IconIDGMail = 0x09, + IconIDFlash = 0x0a, // TODO: what service is this? + IconIDFacebook = 0x0b, + IconIDMusic = 0x0c, + IconIDAlarm = 0x0d, + IconIDWeather = 0x0e, + IconIDGuess = 0x31 + }; + + enum Color { + ColorWhite = 0x00, + ColorBlack = 0x80, + ColorDarkBlue = 0x81, + ColorBlue = 0x82, + ColorLightBlue = 0x83, + ColorDarkGreen = 0x84, + ColorGray = 0x85, + ColorBlue2 = 0x86, + ColorLightBlue2 = 0x87, + ColorGreen = 0x88, + ColorOliveGreen = 0x89, + ColorLightGreen = 0x90, + ColorViolet = 0x91, + ColorViolet2 = 0x91, + ColorBlue3 = 0x92, + ColorBrown = 0x93, + ColorGray2 = 0x94, + ColorBlue4 = 0x95, + ColorBlue5 = 0x96, + ColorRed = 0xA0, + ColorOrange = 0xB8, + ColorYellow = 0xBC + }; + + TimelineAttribute(Type type, const QByteArray &content): + m_type(type), + m_content(content) + {} + + TimelineAttribute(Type type, IconID iconId): + m_type(type) + { + setContent(iconId); + } + TimelineAttribute(Type type, Color color): + m_type(type) + { + setContent(color); + } + TimelineAttribute(Type type, const QStringList &values): + m_type(type) + { + setContent(values); + } + TimelineAttribute(Type type, quint8 data): + m_type(type) + { + setContent(data); + } + + void setContent(const QString &content); + void setContent(IconID iconId); + void setContent(Color color); + void setContent(const QStringList &values); + void setContent(quint8 data); + + QByteArray serialize() const; +private: + Type m_type; + QByteArray m_content; +}; + +class TimelineAction: public PebblePacket +{ +public: + enum Type { + TypeAncsDismiss = 1, + TypeGeneric = 2, + TypeResponse = 3, + TypeDismiss = 4, + TypeHTTP = 5, + TypeSnooze = 6, + TypeOpenWatchApp = 7, + TypeEmpty = 8, + TypeRemove = 9, + TypeOpenPin = 10 + }; + TimelineAction(quint8 actionId, Type type, const QList<TimelineAttribute> &attributes = QList<TimelineAttribute>()); + void appendAttribute(const TimelineAttribute &attribute); + + QByteArray serialize() const override { + QByteArray ret; + ret.append(m_actionId); + ret.append((quint8)m_type); + ret.append(m_attributes.count()); + foreach (const TimelineAttribute &attr, m_attributes) { + ret.append(attr.serialize()); + } + return ret; + } + +private: + quint8 m_actionId; + Type m_type; + QList<TimelineAttribute> m_attributes; +}; + +class TimelineItem: public PebblePacket +{ +public: + enum Type { + TypeNotification = 1, + TypePin = 2, + TypeReminder = 3 + }; + + // TODO: this is probably not complete and maybe even wrong. + enum Flag { + FlagNone = 0x00, + FlagSingleEvent = 0x01, + FlagTimeInUTC = 0x02, + FlagAllDay = 0x04 + }; + Q_DECLARE_FLAGS(Flags, Flag) + + // TODO: This is not complete + enum Layout { + LayoutGenericPin = 0x01, + LayoutCalendar = 0x02 + }; + + TimelineItem(Type type, TimelineItem::Flags flags = FlagNone, const QDateTime ×tamp = QDateTime::currentDateTime(), quint16 duration = 0); + TimelineItem(const QUuid &uuid, Type type, Flags flags = FlagNone, const QDateTime ×tamp = QDateTime::currentDateTime(), quint16 duration = 0); + + QUuid itemId() const; + + void setLayout(quint8 layout); + void setFlags(Flags flags); + + void appendAttribute(const TimelineAttribute &attribute); + void appendAction(const TimelineAction &action); + + QList<TimelineAttribute> attributes() const; + QList<TimelineAction> actions() const; + + QByteArray serialize() const override; + +private: + QUuid m_itemId; + QUuid m_parentId; + QDateTime m_timestamp; + quint16 m_duration = 0; + Type m_type; + Flags m_flags; // quint16 + quint8 m_layout = 0x01; // TODO: find out what this is about + QList<TimelineAttribute> m_attributes; + QList<TimelineAction> m_actions; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(TimelineItem::Flags) + +#endif // TIMELINEITEM_H diff --git a/daemon/watchconnector.cpp b/daemon/watchconnector.cpp index 5f15ee6..4ed8130 100644 --- a/daemon/watchconnector.cpp +++ b/daemon/watchconnector.cpp @@ -10,6 +10,7 @@ #include "unpacker.h" #include "watchconnector.h" +#include "timelineitem.h" static const int RECONNECT_TIMEOUT = 500; //ms static const bool PROTOCOL_DEBUG = false; @@ -600,7 +601,6 @@ QString WatchConnector::timeStamp() void WatchConnector::sendNotification(uint lead, QString sender, QString data, QString subject) { - qCDebug(l) << _versions.main.version; if (_versions.main.version < "v3.0") { QStringList tmp; tmp.append(sender); @@ -613,80 +613,71 @@ void WatchConnector::sendNotification(uint lead, QString sender, QString data, Q sendMessage(watchNOTIFICATION, res); } else { - int source; + TimelineAttribute::IconID iconId = TimelineAttribute::IconIDDefaultBell; + TimelineAttribute::Color color = TimelineAttribute::ColorRed; + QString muteName; switch (lead) { - case leadEMAIL: - source = 19; - break; case leadFACEBOOK: - source = 11; + iconId = TimelineAttribute::IconIDFacebook; + color = TimelineAttribute::ColorBlue; + muteName = "facebook"; break; - case leadSMS: - source = 45; + case leadHANGOUTS: + iconId = TimelineAttribute::IconIDHangout; + color = TimelineAttribute::ColorGreen; + muteName = "Hangout"; + break; + case leadMISSEDCALL: + iconId = TimelineAttribute::IconIDDefaultMissedCall; + muteName = "call notifications"; + break; + case leadTELEGRAM: + iconId = TimelineAttribute::IconIDTelegram; + color = TimelineAttribute::ColorLightBlue; + muteName = "Telegram"; break; case leadTWITTER: - source = 6; + iconId = TimelineAttribute::IconIDTwitter; + color = TimelineAttribute::ColorBlue2; + muteName = "Twitter"; + break; + case leadWHATSAPP: + iconId = TimelineAttribute::IconIDWhatsApp; + color = TimelineAttribute::ColorGreen; + muteName = "WhatsApp"; break; + case leadSMS: + muteName = "SMS"; + iconId = TimelineAttribute::IconIDDefaultBell; + break; + case leadEMAIL: default: - source = 1; + muteName = "e mails"; + iconId = TimelineAttribute::IconIDDefaultBell; + break; } - int attributesCount = 0; - QByteArray attributes; - - attributesCount++; - QByteArray senderBytes = sender.left(64).toUtf8(); - attributes.append(0x01); // id = title - attributes.append(senderBytes.length() & 0xFF); attributes.append(((senderBytes.length() >> 8) & 0xFF)); // length - attributes.append(senderBytes); // content - - attributesCount++; - QByteArray subjectBytes = (subject.isEmpty() ? data : subject).left(64).toUtf8(); - attributes.append(0x02); // id = subtitle - attributes.append(subjectBytes.length() & 0xFF); attributes.append((subjectBytes.length() >> 8) & 0xFF); // length - attributes.append(subjectBytes); //content - - if (!data.isEmpty()) { - attributesCount++; - QByteArray dataBytes = data.left(512).toUtf8(); - attributes.append(0x03); // id = body - attributes.append(dataBytes.length() & 0xFF); attributes.append((dataBytes.length() >> 8) & 0xFF); // length - attributes.append(dataBytes); // content - } + QUuid itemUuid = QUuid::createUuid(); + TimelineItem timelineItem(itemUuid, TimelineItem::TypeNotification); + timelineItem.setFlags(TimelineItem::FlagSingleEvent); + + TimelineAttribute titleAttribute(TimelineAttribute::TypeTitle, sender.left(64).toUtf8()); + timelineItem.appendAttribute(titleAttribute); + + TimelineAttribute subjectAttribute(TimelineAttribute::TypeSubtitle, subject.left(64).toUtf8()); + timelineItem.appendAttribute(subjectAttribute); - attributesCount++; - attributes.append(0x04); // id = tinyicon - attributes.append(0x04); attributes.append('\0'); // length - attributes.append(source); attributes.append('\0'); attributes.append('\0'); attributes.append('\0'); // content - - - QByteArray actions; - actions.append('\0'); // action id - actions.append(0x04); // type = dismiss - actions.append(0x01); // attributes length = 1 - actions.append(0x01); // attribute id = title - actions.append(0x07); actions.append('\0'); // attribute length - actions.append("Dismiss"); // attribute content - - - QByteArray itemId = QUuid::createUuid().toRfc4122(); - int time = QDateTime::currentMSecsSinceEpoch() / 1000; - QByteArray item; - item.append(itemId); // item id - item.append(QUuid().toRfc4122()); // parent id - item.append(time & 0xFF); item.append((time >> 8) & 0xFF); item.append((time >> 16) & 0xFF); item.append((time >> 24) & 0xFF); // timestamp - item.append('\0'); item.append('\0'); // duration - item.append(0x01); // type: notification - item.append('\0'); item.append('\0'); // flags - item.append(0x01); // layout - - int length = attributes.length() + actions.length(); - item.append(length & 0xFF); item.append((length >> 8) & 0xFF); // data length - item.append(attributesCount); // attributes count - item.append(0x01); // actions count - item.append(attributes); - item.append(actions); + TimelineAttribute bodyAttribute(TimelineAttribute::TypeBody, data.toUtf8()); + timelineItem.appendAttribute(bodyAttribute); + TimelineAttribute iconAttribute(TimelineAttribute::TypeTinyIcon, iconId); + timelineItem.appendAttribute(iconAttribute); + + TimelineAttribute colorAttribute(TimelineAttribute::TypeColor, color); + timelineItem.appendAttribute(colorAttribute); + + QByteArray item = timelineItem.serialize(); + QByteArray itemId = itemUuid.toRfc4122(); int token = (qrand() % ((int)pow(2, 16) - 2)) + 1; QByteArray blob; blob.append(0x01); // command = insert @@ -696,10 +687,8 @@ void WatchConnector::sendNotification(uint lead, QString sender, QString data, Q blob.append(itemId); // key blob.append(item.length() & 0xFF); blob.append((item.length() >> 8) & 0xFF); // value length blob.append(item); - sendMessage(watchBLOB_DB, blob); } - } void WatchConnector::sendSMSNotification(QString sender, QString data) @@ -722,6 +711,21 @@ void WatchConnector::sendEmailNotification(QString sender, QString data, QString sendNotification(leadEMAIL, sender, data, subject); } +void WatchConnector::sendWhatsappNotification(QString sender, QString data) +{ + sendNotification(leadWHATSAPP, sender, data, ""); +} + +void WatchConnector::sendTelegramNotification(QString sender, QString data) +{ + sendNotification(leadTELEGRAM, sender, data, ""); +} + +void WatchConnector::sendHangoutsNotification(QString sender, QString data) +{ + sendNotification(leadHANGOUTS, sender, data, ""); +} + void WatchConnector::sendMusicNowPlaying(QString artist, QString album, QString track) { QStringList tmp; diff --git a/daemon/watchconnector.h b/daemon/watchconnector.h index 83e065c..de2ce1a 100644 --- a/daemon/watchconnector.h +++ b/daemon/watchconnector.h @@ -1,294 +1,316 @@ -#ifndef WATCHCONNECTOR_H -#define WATCHCONNECTOR_H - -#include <functional> -#include <QDebug> -#include <QObject> -#include <QPointer> -#include <QStringList> -#include <QTimer> -#include <QDateTime> -#include <QBluetoothSocket> -#include <QBluetoothAddress> -#include <QLoggingCategory> - -class WatchConnector : public QObject -{ - Q_OBJECT - QLoggingCategory l; - - Q_ENUMS(Endpoint) - - Q_PROPERTY(QString name READ name NOTIFY pebbleChanged) - Q_PROPERTY(QString connected READ isConnected NOTIFY connectedChanged) - -public: - enum Endpoint { - watchTIME = 11, - watchVERSION = 16, - watchPHONE_VERSION = 17, - watchSYSTEM_MESSAGE = 18, - watchMUSIC_CONTROL = 32, - watchPHONE_CONTROL = 33, - watchAPPLICATION_MESSAGE = 48, - watchLAUNCHER = 49, - watchLOGS = 2000, - watchPING = 2001, - watchLOG_DUMP = 2002, - watchRESET = 2003, - watchAPP = 2004, - watchAPP_LOGS = 2006, - watchNOTIFICATION = 3000, - watchEXTENSIBLE_NOTIFS = 3010, // Deprecated in 3.x - watchRESOURCE = 4000, - watchFACTORY_SETTINGS = 5001, - watchAPP_MANAGER = 6000, // Deprecated in 3.x - watchAPP_FETCH = 6001, // New in 3.x - watchDATA_LOGGING = 6778, - watchSCREENSHOT = 8000, - watchFILE_MANAGER = 8181, - watchCORE_DUMP = 9000, - watchAUDIO = 10000, // New in 3.x - watchBLOB_DB = 45531, // New in 3.x - watchPUTBYTES = 48879 - }; - enum { - callANSWER = 1, - callHANGUP = 2, - callGET_STATE = 3, - callINCOMING = 4, - callOUTGOING = 5, - callMISSED = 6, - callRING = 7, - callSTART = 8, - callEND = 9 - }; - enum MusicControl { - musicPLAY_PAUSE = 1, - musicPAUSE = 2, - musicPLAY = 3, - musicNEXT = 4, - musicPREVIOUS = 5, - musicVOLUME_UP = 6, - musicVOLUME_DOWN = 7, - musicGET_NOW_PLAYING = 8, - musicSEND_NOW_PLAYING = 9 - }; - enum SystemMessage { - systemFIRMWARE_AVAILABLE = 0, - systemFIRMWARE_START = 1, - systemFIRMWARE_COMPLETE = 2, - systemFIRMWARE_FAIL = 3, - systemFIRMWARE_UP_TO_DATE = 4, - systemFIRMWARE_OUT_OF_DATE = 5, - systemBLUETOOTH_START_DISCOVERABLE = 6, - systemBLUETOOTH_END_DISCOVERABLE = 7 - }; - enum AppManager { - appmgrGET_APPBANK_STATUS = 1, - appmgrREMOVE_APP = 2, - appmgrREFRESH_APP = 3, - appmgrGET_APPBANK_UUIDS = 5 - }; - enum AppMessage { - appmsgPUSH = 1, - appmsgREQUEST = 2, - appmsgACK = 0xFF, - appmsgNACK = 0x7F - }; - enum DataLogMessage { - datalogOPEN = 1, - datalogDATA = 2, - datalogCLOSE = 3, - datalogTIMEOUT = 7 - }; - enum { - launcherSTARTED = 1, - launcherSTOPPED = 0 - }; - enum { - leadEMAIL = 0, - leadSMS = 1, - leadFACEBOOK = 2, - leadTWITTER = 3, - leadNOW_PLAYING_DATA = 16 - }; - enum { - sessionCapGAMMA_RAY = 0x80000000 - }; - enum { - remoteCapTELEPHONY = 16, - remoteCapSMS = 32, - remoteCapGPS = 64, - remoteCapBTLE = 128, - remoteCapCAMERA_REAR = 256, - remoteCapACCEL = 512, - remoteCapGYRO = 1024, - remoteCapCOMPASS = 2048 - }; - enum { - osUNKNOWN = 0, - osIOS = 1, - osANDROID = 2, - osOSX = 3, - osLINUX = 4, - osWINDOWS = 5 - }; - enum UploadType { - uploadFIRMWARE = 1, - uploadRECOVERY = 2, - uploadSYS_RESOURCES = 3, - uploadRESOURCES = 4, - uploadBINARY = 5, - uploadFILE = 6, - uploadWORKER = 7 - }; - enum PutBytesCommand { - putbytesINIT = 1, - putbytesSEND = 2, - putbytesCOMMIT = 3, - putbytesABORT = 4, - putbytesCOMPLETE = 5 - }; - - enum HardwareRevision { - HR_UNKNOWN = 0, - TINTIN_EV1 = 1, - TINTIN_EV2 = 2, - TINTIN_EV2_3 = 3, - TINTIN_EV2_4 = 4, - TINTIN_V1_5 = 5, - BIANCA = 6, - SNOWY_EVT2 = 7, - SNOWY_DVT = 8, - SPALDING_EVT = 9, - BOBBY_SMILES = 10, - SPALDING = 11, - - TINTIN_BB = 0xFF, - TINTIN_BB2 = 0xFE, - SNOWY_BB = 0xFD, - SNOWY_BB2 = 0xFC, - SPALDING_BB2 = 0xFB - }; - enum HardwarePlatform { - HP_UNKNOWN = 0, - APLITE, - BASALT, - CHALK - }; - typedef QPair<HardwarePlatform,QString> HWMap; - QMap<HardwareRevision, HWMap> hardwareMapping; - - struct SoftwareVersion { - QDateTime build; - QString version; - QString commit; - bool is_recovery; - HardwareRevision hw_revision; - QString hw_string; - quint8 metadata_version; - - QVariantMap toMap() const; - }; - - struct WatchVersions { - SoftwareVersion main; - SoftwareVersion safe; - QDateTime bootLoaderBuild; - QString hardwareRevision; - QString hardwarePlatform; - QString serialNumber; - QByteArray address; - - QVariantMap toMap() const; - void clear(); - bool isEmpty() const; - }; - - typedef QMap<int, QVariant> Dict; - enum DictItemType { - typeBYTES, - typeSTRING, - typeUINT, - typeINT - }; - - typedef std::function<bool(const QByteArray &)> EndpointHandlerFunc; - - explicit WatchConnector(QObject *parent = 0); - virtual ~WatchConnector(); - - inline bool isConnected() const { return is_connected; } - inline QString name() const { return pebbles.keys(address()).at(0); } - inline QBluetoothAddress address() const { return socket != nullptr ? socket->peerAddress() : QBluetoothAddress(); } - inline WatchVersions versions() const { return _versions; } - - void setEndpointHandler(uint endpoint, const EndpointHandlerFunc &func); - void clearEndpointHandler(uint endpoint); - - static QString timeStamp(); - static QString decodeEndpoint(uint val); - -signals: - void pebbleChanged(); - void versionsChanged(); - void connectedChanged(); - -public slots: - bool findPebbles(); - void scheduleReconnect(); - void connect(); - void disconnect(); - - void sendMessage(uint endpoint, const QByteArray &data, const EndpointHandlerFunc &callback = EndpointHandlerFunc()); - void ping(uint cookie); - void systemMessage(SystemMessage msg, const EndpointHandlerFunc &callback = EndpointHandlerFunc()); - void time(); - - void sendNotification(uint lead, QString sender, QString data, QString subject); - void sendSMSNotification(QString sender, QString data); - void sendEmailNotification(QString sender, QString data, QString subject); - void sendFacebookNotification(QString sender, QString data); - void sendTwitterNotification(QString sender, QString data); - void sendMusicNowPlaying(QString artist, QString album, QString track); - void sendPhoneVersion(); - void sendFirmwareState(bool ok); - - void buildData(QByteArray &res, QStringList data); - QByteArray buildMessageData(uint lead, QStringList data); - - void phoneControl(char act, uint cookie, QStringList datas); - void ring(QString number, QString name, bool incoming=true, uint cookie=0); - void startPhoneCall(uint cookie=0); - void endPhoneCall(uint cookie=0); - -private slots: - void onReadSocket(); - void onBytesWritten(qint64); - void onConnected(); - void onDisconnected(); - void onError(QBluetoothSocket::SocketError error); - -private: - void sendData(const QByteArray &data); - bool dispatchMessage(uint endpoint, const QByteArray &data); - - QPointer<QBluetoothSocket> socket; - QHash<uint, QList<EndpointHandlerFunc>> tmpHandlers; - QHash<uint, EndpointHandlerFunc> handlers; - bool is_connected; - QByteArray writeData; - QTimer reconnectTimer; - QTimer timeSyncTimer; - QMap<QString,QBluetoothAddress> pebbles; - int currentPebble; - quint64 _last_address; - WatchVersions _versions; - HardwarePlatform platform; -}; - -QDebug operator<< (QDebug d, const WatchConnector::SoftwareVersion &ver); -QDebug operator<< (QDebug d, const WatchConnector::WatchVersions &ver); - -#endif // WATCHCONNECTOR_H +#ifndef WATCHCONNECTOR_H
+#define WATCHCONNECTOR_H
+
+#include <functional>
+#include <QDebug>
+#include <QObject>
+#include <QPointer>
+#include <QStringList>
+#include <QTimer>
+#include <QDateTime>
+#include <QBluetoothSocket>
+#include <QBluetoothAddress>
+#include <QLoggingCategory>
+
+class PebblePacket {
+public:
+ PebblePacket() {}
+ virtual ~PebblePacket() = default;
+ virtual QByteArray serialize() const = 0;
+ QByteArray packString(const QString &string) const {
+ QByteArray tmp = string.left(0xEF).toUtf8();
+ QByteArray ret;
+ ret.append((tmp.length() + 1) & 0xFF);
+ ret.append(tmp);
+ ret.append('\0');
+ return ret;
+ }
+};
+
+class WatchConnector : public QObject
+{
+ Q_OBJECT
+ QLoggingCategory l;
+
+ Q_ENUMS(Endpoint)
+
+ Q_PROPERTY(QString name READ name NOTIFY pebbleChanged)
+ Q_PROPERTY(QString connected READ isConnected NOTIFY connectedChanged)
+
+public:
+ enum Endpoint {
+ watchTIME = 11,
+ watchVERSION = 16,
+ watchPHONE_VERSION = 17,
+ watchSYSTEM_MESSAGE = 18,
+ watchMUSIC_CONTROL = 32,
+ watchPHONE_CONTROL = 33,
+ watchAPPLICATION_MESSAGE = 48,
+ watchLAUNCHER = 49,
+ watchLOGS = 2000,
+ watchPING = 2001,
+ watchLOG_DUMP = 2002,
+ watchRESET = 2003,
+ watchAPP = 2004,
+ watchAPP_LOGS = 2006,
+ watchNOTIFICATION = 3000,
+ watchEXTENSIBLE_NOTIFS = 3010, // Deprecated in 3.x
+ watchRESOURCE = 4000,
+ watchFACTORY_SETTINGS = 5001,
+ watchAPP_MANAGER = 6000, // Deprecated in 3.x
+ watchAPP_FETCH = 6001, // New in 3.x
+ watchDATA_LOGGING = 6778,
+ watchSCREENSHOT = 8000,
+ watchFILE_MANAGER = 8181,
+ watchCORE_DUMP = 9000,
+ watchAUDIO = 10000, // New in 3.x
+ watchBLOB_DB = 45531, // New in 3.x
+ watchPUTBYTES = 48879
+ };
+ enum {
+ callANSWER = 1,
+ callHANGUP = 2,
+ callGET_STATE = 3,
+ callINCOMING = 4,
+ callOUTGOING = 5,
+ callMISSED = 6,
+ callRING = 7,
+ callSTART = 8,
+ callEND = 9
+ };
+ enum MusicControl {
+ musicPLAY_PAUSE = 1,
+ musicPAUSE = 2,
+ musicPLAY = 3,
+ musicNEXT = 4,
+ musicPREVIOUS = 5,
+ musicVOLUME_UP = 6,
+ musicVOLUME_DOWN = 7,
+ musicGET_NOW_PLAYING = 8,
+ musicSEND_NOW_PLAYING = 9
+ };
+ enum SystemMessage {
+ systemFIRMWARE_AVAILABLE = 0,
+ systemFIRMWARE_START = 1,
+ systemFIRMWARE_COMPLETE = 2,
+ systemFIRMWARE_FAIL = 3,
+ systemFIRMWARE_UP_TO_DATE = 4,
+ systemFIRMWARE_OUT_OF_DATE = 5,
+ systemBLUETOOTH_START_DISCOVERABLE = 6,
+ systemBLUETOOTH_END_DISCOVERABLE = 7
+ };
+ enum AppManager {
+ appmgrGET_APPBANK_STATUS = 1,
+ appmgrREMOVE_APP = 2,
+ appmgrREFRESH_APP = 3,
+ appmgrGET_APPBANK_UUIDS = 5
+ };
+ enum AppMessage {
+ appmsgPUSH = 1,
+ appmsgREQUEST = 2,
+ appmsgACK = 0xFF,
+ appmsgNACK = 0x7F
+ };
+ enum DataLogMessage {
+ datalogOPEN = 1,
+ datalogDATA = 2,
+ datalogCLOSE = 3,
+ datalogTIMEOUT = 7
+ };
+ enum {
+ launcherSTARTED = 1,
+ launcherSTOPPED = 0
+ };
+ enum {
+ leadEMAIL = 0,
+ leadSMS = 1,
+ leadFACEBOOK = 2,
+ leadTWITTER = 3,
+ leadTELEGRAM = 4,
+ leadHANGOUTS = 5,
+ leadWHATSAPP = 6,
+ leadMISSEDCALL = 7,
+ leadNOW_PLAYING_DATA = 16
+ };
+ enum {
+ sessionCapGAMMA_RAY = 0x80000000
+ };
+ enum {
+ remoteCapTELEPHONY = 16,
+ remoteCapSMS = 32,
+ remoteCapGPS = 64,
+ remoteCapBTLE = 128,
+ remoteCapCAMERA_REAR = 256,
+ remoteCapACCEL = 512,
+ remoteCapGYRO = 1024,
+ remoteCapCOMPASS = 2048
+ };
+ enum {
+ osUNKNOWN = 0,
+ osIOS = 1,
+ osANDROID = 2,
+ osOSX = 3,
+ osLINUX = 4,
+ osWINDOWS = 5
+ };
+ enum UploadType {
+ uploadFIRMWARE = 1,
+ uploadRECOVERY = 2,
+ uploadSYS_RESOURCES = 3,
+ uploadRESOURCES = 4,
+ uploadBINARY = 5,
+ uploadFILE = 6,
+ uploadWORKER = 7
+ };
+ enum PutBytesCommand {
+ putbytesINIT = 1,
+ putbytesSEND = 2,
+ putbytesCOMMIT = 3,
+ putbytesABORT = 4,
+ putbytesCOMPLETE = 5
+ };
+
+ enum HardwareRevision {
+ HR_UNKNOWN = 0,
+ TINTIN_EV1 = 1,
+ TINTIN_EV2 = 2,
+ TINTIN_EV2_3 = 3,
+ TINTIN_EV2_4 = 4,
+ TINTIN_V1_5 = 5,
+ BIANCA = 6,
+ SNOWY_EVT2 = 7,
+ SNOWY_DVT = 8,
+ SPALDING_EVT = 9,
+ BOBBY_SMILES = 10,
+ SPALDING = 11,
+
+ TINTIN_BB = 0xFF,
+ TINTIN_BB2 = 0xFE,
+ SNOWY_BB = 0xFD,
+ SNOWY_BB2 = 0xFC,
+ SPALDING_BB2 = 0xFB
+ };
+ enum HardwarePlatform {
+ HP_UNKNOWN = 0,
+ APLITE,
+ BASALT,
+ CHALK
+ };
+ typedef QPair<HardwarePlatform,QString> HWMap;
+ QMap<HardwareRevision, HWMap> hardwareMapping;
+
+ struct SoftwareVersion {
+ QDateTime build;
+ QString version;
+ QString commit;
+ bool is_recovery;
+ HardwareRevision hw_revision;
+ QString hw_string;
+ quint8 metadata_version;
+
+ QVariantMap toMap() const;
+ };
+
+ struct WatchVersions {
+ SoftwareVersion main;
+ SoftwareVersion safe;
+ QDateTime bootLoaderBuild;
+ QString hardwareRevision;
+ QString hardwarePlatform;
+ QString serialNumber;
+ QByteArray address;
+
+ QVariantMap toMap() const;
+ void clear();
+ bool isEmpty() const;
+ };
+
+ typedef QMap<int, QVariant> Dict;
+ enum DictItemType {
+ typeBYTES,
+ typeSTRING,
+ typeUINT,
+ typeINT
+ };
+
+ typedef std::function<bool(const QByteArray &)> EndpointHandlerFunc;
+
+ explicit WatchConnector(QObject *parent = 0);
+ virtual ~WatchConnector();
+
+ inline bool isConnected() const { return is_connected; }
+ inline QString name() const { return pebbles.keys(address()).at(0); }
+ inline QBluetoothAddress address() const { return socket != nullptr ? socket->peerAddress() : QBluetoothAddress(); }
+ inline WatchVersions versions() const { return _versions; }
+
+ void setEndpointHandler(uint endpoint, const EndpointHandlerFunc &func);
+ void clearEndpointHandler(uint endpoint);
+
+ static QString timeStamp();
+ static QString decodeEndpoint(uint val);
+
+signals:
+ void pebbleChanged();
+ void versionsChanged();
+ void connectedChanged();
+
+public slots:
+ bool findPebbles();
+ void scheduleReconnect();
+ void connect();
+ void disconnect();
+
+ void sendMessage(uint endpoint, const QByteArray &data, const EndpointHandlerFunc &callback = EndpointHandlerFunc());
+ void ping(uint cookie);
+ void systemMessage(SystemMessage msg, const EndpointHandlerFunc &callback = EndpointHandlerFunc());
+ void time();
+
+ void sendNotification(uint lead, QString sender, QString data, QString subject);
+ void sendSMSNotification(QString sender, QString data);
+ void sendEmailNotification(QString sender, QString data, QString subject);
+ void sendFacebookNotification(QString sender, QString data);
+ void sendTwitterNotification(QString sender, QString data);
+ void sendTelegramNotification(QString sender, QString data);
+ void sendHangoutsNotification(QString sender, QString data);
+ void sendWhatsappNotification(QString sender, QString data);
+ void sendMusicNowPlaying(QString artist, QString album, QString track);
+ void sendPhoneVersion();
+ void sendFirmwareState(bool ok);
+
+ void buildData(QByteArray &res, QStringList data);
+ QByteArray buildMessageData(uint lead, QStringList data);
+
+ void phoneControl(char act, uint cookie, QStringList datas);
+ void ring(QString number, QString name, bool incoming=true, uint cookie=0);
+ void startPhoneCall(uint cookie=0);
+ void endPhoneCall(uint cookie=0);
+
+private slots:
+ void onReadSocket();
+ void onBytesWritten(qint64);
+ void onConnected();
+ void onDisconnected();
+ void onError(QBluetoothSocket::SocketError error);
+
+private:
+ void sendData(const QByteArray &data);
+ bool dispatchMessage(uint endpoint, const QByteArray &data);
+
+ QPointer<QBluetoothSocket> socket;
+ QHash<uint, QList<EndpointHandlerFunc>> tmpHandlers;
+ QHash<uint, EndpointHandlerFunc> handlers;
+ bool is_connected;
+ QByteArray writeData;
+ QTimer reconnectTimer;
+ QTimer timeSyncTimer;
+ QMap<QString,QBluetoothAddress> pebbles;
+ int currentPebble;
+ quint64 _last_address;
+ WatchVersions _versions;
+ HardwarePlatform platform;
+};
+
+QDebug operator<< (QDebug d, const WatchConnector::SoftwareVersion &ver);
+QDebug operator<< (QDebug d, const WatchConnector::WatchVersions &ver);
+
+#endif // WATCHCONNECTOR_H
|
