summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Branson <andrew.branson@cern.ch>2016-02-10 00:15:20 +0100
committerAndrew Branson <andrew.branson@cern.ch>2016-02-10 00:15:20 +0100
commitb3f9fcecdcf5f73ac902d76b95739b76e6bfcba1 (patch)
tree7b787e3c66c893a30e39c7b59f04e5424610cbe9
parent538628b9ee37cf6156abd6f7d86ef1df203fb5f7 (diff)
V3 firmware support improvements
Proper timeline notifications for the v3 firmware. Added telegram, whatapp and hangouts notification types. Removed mitakuuluu.
-rw-r--r--app/pebble.desktop2
-rw-r--r--app/pebblefirmware.cpp18
-rw-r--r--app/qml/pages/ManagerPage.qml622
-rw-r--r--daemon/daemon.pro6
-rw-r--r--daemon/manager.cpp1185
-rw-r--r--daemon/manager.h317
-rw-r--r--daemon/notificationmanager.cpp120
-rw-r--r--daemon/notificationmanager.h3
-rw-r--r--daemon/settings.h170
-rw-r--r--daemon/timelineitem.cpp144
-rw-r--r--daemon/timelineitem.h194
-rw-r--r--daemon/watchconnector.cpp136
-rw-r--r--daemon/watchconnector.h610
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 &timestamp, quint16 duration):
+ TimelineItem(QUuid::createUuid(), type, flags, timestamp, duration)
+{
+
+}
+
+TimelineItem::TimelineItem(const QUuid &uuid, TimelineItem::Type type, Flags flags, const QDateTime &timestamp, 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 &timestamp = QDateTime::currentDateTime(), quint16 duration = 0);
+ TimelineItem(const QUuid &uuid, Type type, Flags flags = FlagNone, const QDateTime &timestamp = 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