From 1f0cde7cfd31c180eaceeab4ee0ad24613eaf34c Mon Sep 17 00:00:00 2001 From: Tomasz Sterna Date: Sat, 21 Jun 2014 22:23:07 +0200 Subject: Restructured for lib and daemon --- .gitignore | 3 + app/app.pro | 16 +++ app/qml/cover/CoverPage.qml | 47 +++++++ app/qml/pages/WatchPage.qml | 282 +++++++++++++++++++++++++++++++++++++++++ app/qml/waterwatch.qml | 42 +++++++ app/waterwatch.cpp | 46 +++++++ app/waterwatch.desktop | 7 ++ app/waterwatch.png | Bin 0 -> 1725 bytes daemon/daemon.cpp | 33 +++++ daemon/daemon.pro | 13 ++ lib/lib.pro | 11 ++ lib/watchconnector.cpp | 296 +++++++++++++++++++++++++++++++++++++++++++ lib/watchconnector.h | 129 +++++++++++++++++++ qml/cover/CoverPage.qml | 47 ------- qml/pages/WatchPage.qml | 282 ----------------------------------------- qml/waterwatch.qml | 42 ------- rpm/waterwatch.yaml | 6 +- src/watchconnector.cpp | 300 -------------------------------------------- src/watchconnector.h | 133 -------------------- src/waterwatch.cpp | 44 ------- waterwatch.desktop | 7 -- waterwatch.png | Bin 1725 -> 0 bytes waterwatch.pro | 24 +--- 23 files changed, 931 insertions(+), 879 deletions(-) create mode 100644 app/app.pro create mode 100644 app/qml/cover/CoverPage.qml create mode 100644 app/qml/pages/WatchPage.qml create mode 100644 app/qml/waterwatch.qml create mode 100644 app/waterwatch.cpp create mode 100644 app/waterwatch.desktop create mode 100644 app/waterwatch.png create mode 100644 daemon/daemon.cpp create mode 100644 daemon/daemon.pro create mode 100644 lib/lib.pro create mode 100644 lib/watchconnector.cpp create mode 100644 lib/watchconnector.h delete mode 100644 qml/cover/CoverPage.qml delete mode 100644 qml/pages/WatchPage.qml delete mode 100644 qml/waterwatch.qml delete mode 100644 src/watchconnector.cpp delete mode 100644 src/watchconnector.h delete mode 100644 src/waterwatch.cpp delete mode 100644 waterwatch.desktop delete mode 100644 waterwatch.png diff --git a/.gitignore b/.gitignore index 620d3dc..fdabbee 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,6 @@ *.lai *.la *.a + +/rpm/*.spec +/*.pro.user diff --git a/app/app.pro b/app/app.pro new file mode 100644 index 0000000..c7e6928 --- /dev/null +++ b/app/app.pro @@ -0,0 +1,16 @@ +TARGET = waterwatch + +CONFIG += sailfishapp + +SOURCES += waterwatch.cpp + +INCLUDEPATH += ../lib +LIBS += -L$$OUT_PWD/../lib -lpebble + +QT += bluetooth +QMAKE_CXXFLAGS += -std=c++0x + +OTHER_FILES += qml/waterwatch.qml \ + qml/cover/CoverPage.qml \ + waterwatch.desktop \ + qml/pages/WatchPage.qml diff --git a/app/qml/cover/CoverPage.qml b/app/qml/cover/CoverPage.qml new file mode 100644 index 0000000..aaa33c6 --- /dev/null +++ b/app/qml/cover/CoverPage.qml @@ -0,0 +1,47 @@ +/* + Copyright (C) 2014 Jouni Roivas + Copyright (C) 2013 Jolla Ltd. + Contact: Thomas Perl + All rights reserved. + + You may use this file under the terms of BSD license as follows: + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the authors nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR + ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +import QtQuick 2.0 +import Sailfish.Silica 1.0 + +CoverBackground { + Label { + id: label + anchors.centerIn: parent + text: "WaterWatch" + } + Label { + anchors.top: label.bottom + anchors.horizontalCenter: parent.horizontalCenter + text: watchPage.watchConnector.name + font.pointSize: Theme.fontSizeSmall + } +} diff --git a/app/qml/pages/WatchPage.qml b/app/qml/pages/WatchPage.qml new file mode 100644 index 0000000..623c47e --- /dev/null +++ b/app/qml/pages/WatchPage.qml @@ -0,0 +1,282 @@ +/* + Copyright (C) 2014 Jouni Roivas + Copyright (C) 2013 Jolla Ltd. + Contact: Thomas Perl + All rights reserved. + + You may use this file under the terms of BSD license as follows: + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the authors nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR + ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +import QtQuick 2.0 +import QtQml 2.1 +import Sailfish.Silica 1.0 +import QtBluetooth 5.0 +import Sailfish.Bluetooth 1.0 +import Bluetooth 0.0 +import org.nemomobile.voicecall 1.0 +import org.nemomobile.notifications 1.0 +import org.nemomobile.messages.internal 1.0 +import org.nemomobile.commhistory 1.0 +import watch 0.1 + +Page { + id: page + property alias watchConnector: watchConnector + WatchConnector { + id: watchConnector + + onHangup: { + // Watch instantiated hangup, follow the orders + manager.hangupAll(); + } + } + property var callerDetails: new Object + + // This actually handles the voice call in SailfisOS + VoiceCallManager { + id:manager + + function hangupAll() { + for (var index = 0; index < voiceCalls.count; index++) { + voiceCalls.instance(index).hangup(); + } + } + + onError: { + console.log("Error: "+message) + watchConnector.endPhoneCall(); + } + + function updateState() { + //This needs cleaning up... + var statusPriority = [ + VoiceCall.STATUS_ALERTING, + VoiceCall.STATUS_DIALING, + VoiceCall.STATUS_DISCONNECTED, + VoiceCall.STATUS_ACTIVE, + VoiceCall.STATUS_HELD, + VoiceCall.STATUS_NULL + ] + + var newPrimaryCall = null + for (var p = 0; p < statusPriority.length; p++) { + for (var i = 0; i < voiceCalls.count; i++) { + if (voiceCalls.instance(i).status === statusPriority[p]) { + newPrimaryCall = voiceCalls.instance(i) + break + } + } + if (newPrimaryCall) { + break + } + } + var person = ""; + if (newPrimaryCall && callerDetails[newPrimaryCall.handlerId]) { + person = callerDetails[newPrimaryCall.handlerId].person + } + + for (var ic = 0; ic < voiceCalls.count; ic++) { + var call = voiceCalls.instance(ic) + console.log("call: " + call.lineId + " state: " + call.statusText + " " + call.status + " " + call.handlerId + "\n") + if (call.status === VoiceCall.STATUS_ALERTING || call.status === VoiceCall.STATUS_DIALING) { + console.log("Tell outgoing: " + call.lineId); + //FIXME: Not working? + watchConnector.ring(call.lineId, person, 0); + } else if (call.status === VoiceCall.STATUS_INCOMING || call.status === VoiceCall.STATUS_WAITING) { + console.log("Tell incoming: " + call.lineId); + watchConnector.ring(call.lineId, person); + } else if (call.status === VoiceCall.STATUS_DISCONNECTED || call.status === VoiceCall.STATUS_NULL) { + console.log("Endphone"); + watchConnector.endPhoneCall(); + } else if (call.status === VoiceCall.ACTIVE) { + console.log("Startphone"); + watchConnector.startPhoneCall(); + } + } + } + } + Timer { + id: updateStateTimer + interval: 50 + onTriggered: manager.updateState() + } + + // Receive the calls + Instantiator { + id: callMonitor + model: manager.voiceCalls + + delegate: QtObject { + property string callStatus: instance.status + property string remoteUid + property string person: "" + onCallStatusChanged: { + console.log("Status changed: " + callStatus); + if (callStatus === VoiceCall.STATUS_DISCONNECTED || callStatus === VoiceCall.STATUS_NULL) { + watchConnector.endPhoneCall(); + manager.updateState(); + } else { + updateStateTimer.start(); + } + } + Component.onCompleted: { + remoteUid = instance.lineId + person = remoteUid !== "" ? people.personByPhoneNumber(remoteUid) : "" + manager.updateState() + } + } + onObjectAdded: { + callerDetails[object.handlerId] = object + } + onObjectRemoved: { + delete callerDetails[object.handlerId] + manager.updateState() + } + } + + // Handle SMS (and other messages) via groups + CommGroupManager { + id: groupManager + useBackgroundThread: true + } + CommContactGroupModel { + id: groupModel + manager: groupManager + + property var unreadGroups: [ ] + + onContactGroupCreated: { + if (group.unreadMessages > 0) { + unreadGroups.push(group) + unreadSignalTimer.start() + } + } + + onContactGroupChanged: { + var index = unreadGroups.indexOf(group) + if (group.unreadMessages > 0 && index < 0) { + unreadGroups.push(group) + unreadSignalTimer.start() + } else if (group.unreadMessages === 0 && index >= 0) { + unreadGroups.splice(index, 1) + unreadSignalTimer.start() + } + } + + onContactGroupRemoved: { + var index = unreadGroups.indexOf(group) + if (index >= 0) { + unreadGroups.splice(index, 1) + unreadSignalTimer.start() + } + } + + onUnreadGroupsChanged: { + var group = groupModel.unreadGroups[0]; + if (group != undefined) { + var name = group.contactNames.length ? group.contactNames[0] : group.groups[0].remoteUids[0]; + console.log("Msg: " + group.lastMessageText); + console.log("From: " + name); + watchConnector.sendSMSNotification(name?name:"Unknown", group.lastMessageText) + } + } + } + Timer { + id: unreadSignalTimer + interval: 1 + onTriggered: groupModel.unreadGroupsChanged() + } + + + SilicaFlickable { + anchors.fill: parent + + contentHeight: column.height + + Column { + id: column + + width: page.width + spacing: Theme.paddingLarge + PageHeader { + title: "WaterWatch" + } + Label { + visible: !watchConnector.isConnected + text: "Waiting for watch...\nIf it can't be found plase\ncheck it's available and\npaired in Bluetooth settings." + width: column.width + } + // Select the device + Repeater { + model: KnownDevicesModel { id: knownDevicesModel } + delegate: ListItem { + id: pairedItem + visible: (model.paired && watchConnector.isConnected) + Label { + text: model.alias.length ? model.alias : model.address + } + onVisibleChanged: { + if (pairedItem.visible) { + // Connect with the device + watchConnector.deviceConnect(model.alias, model.address); + } + } + } + } + Button { + text: "Ping" + onClicked: { + watchConnector.ping(66) + } + } + Button { + text: "Send SMS" + onClicked: { + watchConnector.sendSMSNotification("Dummy", "Hello world!") + } + } + Button { + text: "Ring" + onClicked: { + watchConnector.ring("+1234567890", "Test user") + } + } + Button { + text: "Start call" + onClicked: { + watchConnector.startPhoneCall() + } + } + Button { + text: "End call" + onClicked: { + watchConnector.endPhoneCall() + } + } + } + } +} + + diff --git a/app/qml/waterwatch.qml b/app/qml/waterwatch.qml new file mode 100644 index 0000000..cf65076 --- /dev/null +++ b/app/qml/waterwatch.qml @@ -0,0 +1,42 @@ +/* + Copyright (C) 2014 Jouni Roivas + Copyright (C) 2013 Jolla Ltd. + Contact: Thomas Perl + All rights reserved. + + You may use this file under the terms of BSD license as follows: + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the authors nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR + ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +import QtQuick 2.0 +import Sailfish.Silica 1.0 +import "pages" + +ApplicationWindow +{ + initialPage: WatchPage { id: watchPage } + cover: Qt.resolvedUrl("cover/CoverPage.qml") +} + + diff --git a/app/waterwatch.cpp b/app/waterwatch.cpp new file mode 100644 index 0000000..46e6298 --- /dev/null +++ b/app/waterwatch.cpp @@ -0,0 +1,46 @@ +/* + Copyright (C) 2014 Jouni Roivas + Copyright (C) 2013 Jolla Ltd. + Contact: Thomas Perl + All rights reserved. + + You may use this file under the terms of BSD license as follows: + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the authors nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR + ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include + +#include +#include "watchconnector.h" + +using namespace watch; + +int main(int argc, char *argv[]) +{ + // Registert WatchController object on QML side + qmlRegisterType("watch", 0, 1, "WatchConnector"); + + return SailfishApp::main(argc, argv); +} + diff --git a/app/waterwatch.desktop b/app/waterwatch.desktop new file mode 100644 index 0000000..4ddba76 --- /dev/null +++ b/app/waterwatch.desktop @@ -0,0 +1,7 @@ +[Desktop Entry] +Type=Application +X-Nemo-Application-Type=silica-qt5 +Name=waterwatch +Icon=waterwatch +Exec=waterwatch + diff --git a/app/waterwatch.png b/app/waterwatch.png new file mode 100644 index 0000000..f4aaeeb Binary files /dev/null and b/app/waterwatch.png differ diff --git a/daemon/daemon.cpp b/daemon/daemon.cpp new file mode 100644 index 0000000..c50af39 --- /dev/null +++ b/daemon/daemon.cpp @@ -0,0 +1,33 @@ +/* + Copyright (C) 2014 Tomasz Sterna + + You may use this file under the terms of BSD license as follows: + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the authors nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR + ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "watchconnector.h" + +int main(int argc, char *argv[]) +{ +} diff --git a/daemon/daemon.pro b/daemon/daemon.pro new file mode 100644 index 0000000..d1fb5ca --- /dev/null +++ b/daemon/daemon.pro @@ -0,0 +1,13 @@ +TARGET = pebbled + +CONFIG += console +CONFIG -= app_bundle + +INCLUDEPATH += ../lib +LIBS += -L$$OUT_PWD/../lib -lpebble + +QT += bluetooth +QMAKE_CXXFLAGS += -std=c++0x + +SOURCES += \ + daemon.cpp diff --git a/lib/lib.pro b/lib/lib.pro new file mode 100644 index 0000000..3ef513d --- /dev/null +++ b/lib/lib.pro @@ -0,0 +1,11 @@ +TEMPLATE = lib +TARGET = pebble + +HEADERS += \ + watchconnector.h + +SOURCES += \ + watchconnector.cpp + +QT += bluetooth +QMAKE_CXXFLAGS += -std=c++0x diff --git a/lib/watchconnector.cpp b/lib/watchconnector.cpp new file mode 100644 index 0000000..e987cce --- /dev/null +++ b/lib/watchconnector.cpp @@ -0,0 +1,296 @@ +#include "watchconnector.h" +#include +#include + +using namespace watch; + +static int __reconnect_timeout = 1000; + +WatchConnector::WatchConnector(QObject *parent) : + QObject(parent) +{ + socket = nullptr; +} + +WatchConnector::~WatchConnector() +{ +} + +void WatchConnector::deviceDiscovered(const QBluetoothDeviceInfo &device) +{ + //FIXME TODO: Configurable + if (device.name().startsWith("Pebble")) { + qDebug() << "Found Pebble:" << device.name() << '(' << device.address().toString() << ')'; + handleWatch(device); + } else { + qDebug() << "Found other device:" << device.name() << '(' << device.address().toString() << ')'; + } +} + +void WatchConnector::deviceConnect(const QString name, const QString address) +{ + if (name.startsWith("Pebble")) { + _last_name = name; + _last_address = address; + QBluetoothDeviceInfo device(QBluetoothAddress(address), name, 0); + deviceDiscovered(device); + } +} + +void WatchConnector::reconnect() +{ + if (_last_name != "" && _last_address != "") { + deviceConnect(_last_name, _last_address); + } +} + +void WatchConnector::handleWatch(const QBluetoothDeviceInfo &device) +{ + qDebug() << "handleWatch" << device.name(); + if (socket != nullptr) { + socket->close(); + socket->deleteLater(); + socket = nullptr; + } + + socket = new QBluetoothSocket(QBluetoothSocket::RfcommSocket); + qDebug() << "Creating socket"; + + // FIXME: Assuming port 1 (with Pebble) + socket->connectToService(device.address(), 1); + + connect(socket, SIGNAL(readyRead()), this, SLOT(readSocket())); + connect(socket, SIGNAL(connected()), this, SLOT(connected())); + connect(socket, SIGNAL(disconnected()), this, SLOT(disconnected())); +} + +QString WatchConnector::decodeEndpoint(unsigned int val) +{ + //FIXME: Create a map of these values + switch(val) { + case watchTIME: + return "TIME"; + case watchVERSION: + return "VERSION"; + case watchPHONE_VERSION: + return "PHONE_VERSION"; + case watchSYSTEM_MESSAGE: + return "SYSTEM_MESSAGE"; + case watchMUSIC_CONTROL: + return "MUSIC_CONTROL"; + case watchPHONE_CONTROL: + return "PHONE_CONTROL"; + case watchAPPLICATION_MESSAGE: + return "APP_MSG"; + case watchLAUNCHER: + return "LAUNCHER"; + case watchLOGS: + return "LOGS"; + case watchPING: + return "PING"; + case watchLOG_DUMP: + return "DUMP"; + case watchRESET: + return "RESET"; + case watchAPP: + return "APP"; + case watchAPP_LOGS: + return "APP_LOGS"; + case watchNOTIFICATION: + return "NOTIFICATION"; + case watchRESOURCE: + return "RESOURCE"; + case watchAPP_MANAGER: + return "APP_MANAG"; + case watchSCREENSHOT: + return "SCREENSHOT"; + case watchPUTBYTES: + return "PUTBYTES"; + default: + return "Unknown: "+ QString::number(val); + } +} + +void WatchConnector::decodeMsg(QByteArray data) +{ + unsigned int datalen = 0; + int index = 0; + datalen = (data.at(index) << 8) + data.at(index+1); + index += 2; + + unsigned int endpoint = 0; + endpoint = (data.at(index) << 8) + data.at(index+1); + index += 2; + + qDebug() << "Length:" << datalen << " Endpoint:" << decodeEndpoint(endpoint); + qDebug() << "Data:" << data.mid(index).toHex(); + if (endpoint == watchPHONE_CONTROL) { + if (data.length() >= 5) { + if (data.at(4) == callHANGUP) { + emit hangup(); + } + } + } +} + +void WatchConnector::readSocket() +{ + qDebug() << "read"; + + QBluetoothSocket *socket = qobject_cast(sender()); + if (!socket) return; + + while (socket->bytesAvailable()) { + QByteArray line = socket->readAll(); + emit messageReceived(socket->peerName(), QString::fromUtf8(line.constData(), line.length())); + decodeMsg(line); + } +} + +void WatchConnector::connected() +{ + qDebug() << "Connected!"; + is_connected = true; + emit nameChanged(); + emit connectedChanged(); +} + +void WatchConnector::disconnected() +{ + qDebug() << "Disconnected!"; + is_connected = false; + + QBluetoothSocket *socket = qobject_cast(sender()); + if (!socket) return; + + socket->deleteLater(); + socket = nullptr; + emit connectedChanged(); + emit nameChanged(); + + // Try to connect again after a timeout + QTimer::singleShot(__reconnect_timeout, this, SLOT(reconnect())); +} + +void WatchConnector::sendData(const QByteArray &data) +{ + if (socket == nullptr) return; + + socket->write(data); +} + +void WatchConnector::sendMessage(unsigned int endpoint, QByteArray data) +{ + qDebug() << "Sending message"; + QByteArray msg; + + // First send the length + msg.append((data.length() & 0xFF00) >> 8); + msg.append(data.length() & 0xFF); + + // Then the endpoint + msg.append((endpoint & 0xFF00) >> 8); + msg.append(endpoint & 0xFF); + + // Finally the data + msg.append(data); + + sendData(msg); +} + +void WatchConnector::buildData(QByteArray &res, QStringList data) +{ + for (QString d : data) + { + QByteArray tmp = d.left(0xF0).toUtf8(); + res.append(tmp.length() & 0xFF); + res.append(tmp); + } +} + +QByteArray WatchConnector::buildMessageData(unsigned int lead, QStringList data) +{ + QByteArray res; + res.append(lead & 0xFF); + buildData(res, data); + + return res; +} + +void WatchConnector::ping(unsigned int val) +{ + QByteArray res; + res.append((char)0); + + res.append((char)((val >> 24) & 0xff)); + res.append((char)((val >> 16) & 0xff)); + res.append((char)((val >> 8) & 0xff)); + res.append((char)(val & 0xff)); + + sendMessage(watchPING, res); +} + +QString WatchConnector::timeStamp() +{ + return QString::number(QDateTime::currentMSecsSinceEpoch()); +} + +void WatchConnector::sendNotification(unsigned int lead, QString sender, QString data, QString subject) +{ + QStringList tmp; + tmp.append(sender); + tmp.append(data); + tmp.append(timeStamp()); + if (lead == 0) tmp.append(subject); + + QByteArray res = buildMessageData(lead, tmp); + + sendMessage(watchNOTIFICATION, res); +} + +void WatchConnector::sendSMSNotification(QString sender, QString data) +{ + sendNotification(1, sender, data, ""); +} + +void WatchConnector::sendEmailNotification(QString sender, QString data, QString subject) +{ + sendNotification(0, sender, data, subject); +} + +void WatchConnector::phoneControl(char act, unsigned int cookie, QStringList datas) +{ + QByteArray head; + head.append((char)act); + head.append((cookie >> 24)& 0xFF); + head.append((cookie >> 16)& 0xFF); + head.append((cookie >> 8)& 0xFF); + head.append(cookie & 0xFF); + if (datas.length()>0) buildData(head, datas); + + sendMessage(watchPHONE_CONTROL, head); +} + +void WatchConnector::ring(QString number, QString name, bool incoming, unsigned int cookie) +{ + QStringList tmp; + tmp.append(number); + tmp.append(name); + + char act = callINCOMING; + if (!incoming) { + act = callOUTGOING; + } + + phoneControl(act, cookie, tmp); +} + +void WatchConnector::startPhoneCall(unsigned int cookie) +{ + phoneControl(callSTART, cookie, QStringList()); +} + +void WatchConnector::endPhoneCall(unsigned int cookie) +{ + phoneControl(callEND, cookie, QStringList()); +} diff --git a/lib/watchconnector.h b/lib/watchconnector.h new file mode 100644 index 0000000..616b9f5 --- /dev/null +++ b/lib/watchconnector.h @@ -0,0 +1,129 @@ +/* + Copyright (C) 2014 Jouni Roivas + All rights reserved. + + You may use this file under the terms of BSD license as follows: + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the authors nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR + ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef WATCHCONNECTOR_H +#define WATCHCONNECTOR_H + +#include +#include +#include +#include +#include + +using namespace QtBluetooth; + +namespace watch +{ + +class WatchConnector : public QObject +{ + Q_OBJECT + Q_PROPERTY(QString name READ name NOTIFY nameChanged) + Q_PROPERTY(QString isConnected READ isConnected NOTIFY connectedChanged) +public: + enum { + 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, + watchRESOURCE = 4000, + watchAPP_MANAGER = 6000, + watchSCREENSHOT = 8000, + watchPUTBYTES = 48879 + }; + enum { + callANSWER = 1, + callHANGUP = 2, + callGET_STATE = 3, + callINCOMING = 4, + callOUTGOING = 5, + callMISSED = 6, + callRING = 7, + callSTART = 8, + callEND = 9 + }; + explicit WatchConnector(QObject *parent = 0); + virtual ~WatchConnector(); + bool isConnected() const { return is_connected; } + QString name() const { if (socket != nullptr) return socket->peerName(); return ""; } + + QString timeStamp(); + QString decodeEndpoint(unsigned int val); + +signals: + void messageReceived(QString peer, QString msg); + void nameChanged(); + void connectedChanged(); + void hangup(); + +public slots: + void sendData(const QByteArray &data); + void sendMessage(unsigned int endpoint, QByteArray data); + void ping(unsigned int val); + void sendNotification(unsigned int lead, QString sender, QString data, QString subject); + void sendSMSNotification(QString sender, QString data); + void sendEmailNotification(QString sender, QString data, QString subject); + + void buildData(QByteArray &res, QStringList data); + QByteArray buildMessageData(unsigned int lead, QStringList data); + + void phoneControl(char act, unsigned int cookie, QStringList datas); + void ring(QString number, QString name, bool incoming=true, unsigned int cookie=0); + void startPhoneCall(unsigned int cookie=0); + void endPhoneCall(unsigned int cookie=0); + + void deviceConnect(const QString name, const QString address); + void deviceDiscovered(const QBluetoothDeviceInfo&); + void handleWatch(const QBluetoothDeviceInfo&); + void readSocket(); + void connected(); + void disconnected(); + void reconnect(); + +private: + void decodeMsg(QByteArray data); + QBluetoothSocket *socket; + bool is_connected; + QString _last_name; + QString _last_address; +}; +} + +#endif // WATCHCONNECTOR_H diff --git a/qml/cover/CoverPage.qml b/qml/cover/CoverPage.qml deleted file mode 100644 index aaa33c6..0000000 --- a/qml/cover/CoverPage.qml +++ /dev/null @@ -1,47 +0,0 @@ -/* - Copyright (C) 2014 Jouni Roivas - Copyright (C) 2013 Jolla Ltd. - Contact: Thomas Perl - All rights reserved. - - You may use this file under the terms of BSD license as follows: - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of the authors nor the - names of its contributors may be used to endorse or promote products - derived from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR - ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -import QtQuick 2.0 -import Sailfish.Silica 1.0 - -CoverBackground { - Label { - id: label - anchors.centerIn: parent - text: "WaterWatch" - } - Label { - anchors.top: label.bottom - anchors.horizontalCenter: parent.horizontalCenter - text: watchPage.watchConnector.name - font.pointSize: Theme.fontSizeSmall - } -} diff --git a/qml/pages/WatchPage.qml b/qml/pages/WatchPage.qml deleted file mode 100644 index 623c47e..0000000 --- a/qml/pages/WatchPage.qml +++ /dev/null @@ -1,282 +0,0 @@ -/* - Copyright (C) 2014 Jouni Roivas - Copyright (C) 2013 Jolla Ltd. - Contact: Thomas Perl - All rights reserved. - - You may use this file under the terms of BSD license as follows: - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of the authors nor the - names of its contributors may be used to endorse or promote products - derived from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR - ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -import QtQuick 2.0 -import QtQml 2.1 -import Sailfish.Silica 1.0 -import QtBluetooth 5.0 -import Sailfish.Bluetooth 1.0 -import Bluetooth 0.0 -import org.nemomobile.voicecall 1.0 -import org.nemomobile.notifications 1.0 -import org.nemomobile.messages.internal 1.0 -import org.nemomobile.commhistory 1.0 -import watch 0.1 - -Page { - id: page - property alias watchConnector: watchConnector - WatchConnector { - id: watchConnector - - onHangup: { - // Watch instantiated hangup, follow the orders - manager.hangupAll(); - } - } - property var callerDetails: new Object - - // This actually handles the voice call in SailfisOS - VoiceCallManager { - id:manager - - function hangupAll() { - for (var index = 0; index < voiceCalls.count; index++) { - voiceCalls.instance(index).hangup(); - } - } - - onError: { - console.log("Error: "+message) - watchConnector.endPhoneCall(); - } - - function updateState() { - //This needs cleaning up... - var statusPriority = [ - VoiceCall.STATUS_ALERTING, - VoiceCall.STATUS_DIALING, - VoiceCall.STATUS_DISCONNECTED, - VoiceCall.STATUS_ACTIVE, - VoiceCall.STATUS_HELD, - VoiceCall.STATUS_NULL - ] - - var newPrimaryCall = null - for (var p = 0; p < statusPriority.length; p++) { - for (var i = 0; i < voiceCalls.count; i++) { - if (voiceCalls.instance(i).status === statusPriority[p]) { - newPrimaryCall = voiceCalls.instance(i) - break - } - } - if (newPrimaryCall) { - break - } - } - var person = ""; - if (newPrimaryCall && callerDetails[newPrimaryCall.handlerId]) { - person = callerDetails[newPrimaryCall.handlerId].person - } - - for (var ic = 0; ic < voiceCalls.count; ic++) { - var call = voiceCalls.instance(ic) - console.log("call: " + call.lineId + " state: " + call.statusText + " " + call.status + " " + call.handlerId + "\n") - if (call.status === VoiceCall.STATUS_ALERTING || call.status === VoiceCall.STATUS_DIALING) { - console.log("Tell outgoing: " + call.lineId); - //FIXME: Not working? - watchConnector.ring(call.lineId, person, 0); - } else if (call.status === VoiceCall.STATUS_INCOMING || call.status === VoiceCall.STATUS_WAITING) { - console.log("Tell incoming: " + call.lineId); - watchConnector.ring(call.lineId, person); - } else if (call.status === VoiceCall.STATUS_DISCONNECTED || call.status === VoiceCall.STATUS_NULL) { - console.log("Endphone"); - watchConnector.endPhoneCall(); - } else if (call.status === VoiceCall.ACTIVE) { - console.log("Startphone"); - watchConnector.startPhoneCall(); - } - } - } - } - Timer { - id: updateStateTimer - interval: 50 - onTriggered: manager.updateState() - } - - // Receive the calls - Instantiator { - id: callMonitor - model: manager.voiceCalls - - delegate: QtObject { - property string callStatus: instance.status - property string remoteUid - property string person: "" - onCallStatusChanged: { - console.log("Status changed: " + callStatus); - if (callStatus === VoiceCall.STATUS_DISCONNECTED || callStatus === VoiceCall.STATUS_NULL) { - watchConnector.endPhoneCall(); - manager.updateState(); - } else { - updateStateTimer.start(); - } - } - Component.onCompleted: { - remoteUid = instance.lineId - person = remoteUid !== "" ? people.personByPhoneNumber(remoteUid) : "" - manager.updateState() - } - } - onObjectAdded: { - callerDetails[object.handlerId] = object - } - onObjectRemoved: { - delete callerDetails[object.handlerId] - manager.updateState() - } - } - - // Handle SMS (and other messages) via groups - CommGroupManager { - id: groupManager - useBackgroundThread: true - } - CommContactGroupModel { - id: groupModel - manager: groupManager - - property var unreadGroups: [ ] - - onContactGroupCreated: { - if (group.unreadMessages > 0) { - unreadGroups.push(group) - unreadSignalTimer.start() - } - } - - onContactGroupChanged: { - var index = unreadGroups.indexOf(group) - if (group.unreadMessages > 0 && index < 0) { - unreadGroups.push(group) - unreadSignalTimer.start() - } else if (group.unreadMessages === 0 && index >= 0) { - unreadGroups.splice(index, 1) - unreadSignalTimer.start() - } - } - - onContactGroupRemoved: { - var index = unreadGroups.indexOf(group) - if (index >= 0) { - unreadGroups.splice(index, 1) - unreadSignalTimer.start() - } - } - - onUnreadGroupsChanged: { - var group = groupModel.unreadGroups[0]; - if (group != undefined) { - var name = group.contactNames.length ? group.contactNames[0] : group.groups[0].remoteUids[0]; - console.log("Msg: " + group.lastMessageText); - console.log("From: " + name); - watchConnector.sendSMSNotification(name?name:"Unknown", group.lastMessageText) - } - } - } - Timer { - id: unreadSignalTimer - interval: 1 - onTriggered: groupModel.unreadGroupsChanged() - } - - - SilicaFlickable { - anchors.fill: parent - - contentHeight: column.height - - Column { - id: column - - width: page.width - spacing: Theme.paddingLarge - PageHeader { - title: "WaterWatch" - } - Label { - visible: !watchConnector.isConnected - text: "Waiting for watch...\nIf it can't be found plase\ncheck it's available and\npaired in Bluetooth settings." - width: column.width - } - // Select the device - Repeater { - model: KnownDevicesModel { id: knownDevicesModel } - delegate: ListItem { - id: pairedItem - visible: (model.paired && watchConnector.isConnected) - Label { - text: model.alias.length ? model.alias : model.address - } - onVisibleChanged: { - if (pairedItem.visible) { - // Connect with the device - watchConnector.deviceConnect(model.alias, model.address); - } - } - } - } - Button { - text: "Ping" - onClicked: { - watchConnector.ping(66) - } - } - Button { - text: "Send SMS" - onClicked: { - watchConnector.sendSMSNotification("Dummy", "Hello world!") - } - } - Button { - text: "Ring" - onClicked: { - watchConnector.ring("+1234567890", "Test user") - } - } - Button { - text: "Start call" - onClicked: { - watchConnector.startPhoneCall() - } - } - Button { - text: "End call" - onClicked: { - watchConnector.endPhoneCall() - } - } - } - } -} - - diff --git a/qml/waterwatch.qml b/qml/waterwatch.qml deleted file mode 100644 index cf65076..0000000 --- a/qml/waterwatch.qml +++ /dev/null @@ -1,42 +0,0 @@ -/* - Copyright (C) 2014 Jouni Roivas - Copyright (C) 2013 Jolla Ltd. - Contact: Thomas Perl - All rights reserved. - - You may use this file under the terms of BSD license as follows: - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of the authors nor the - names of its contributors may be used to endorse or promote products - derived from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR - ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -import QtQuick 2.0 -import Sailfish.Silica 1.0 -import "pages" - -ApplicationWindow -{ - initialPage: WatchPage { id: watchPage } - cover: Qt.resolvedUrl("cover/CoverPage.qml") -} - - diff --git a/rpm/waterwatch.yaml b/rpm/waterwatch.yaml index e2f0359..8d3cb49 100644 --- a/rpm/waterwatch.yaml +++ b/rpm/waterwatch.yaml @@ -6,7 +6,7 @@ Group: Qt/Qt URL: http://example.org/ License: BSD Sources: -- '%{name}-%{version}.tar.bz2' +- '%{name}-%{version}.tar.xz' Description: | Include support for Pebble watch to receive event from SailfishOS device. Communicates via Bluetooth, supporting the Pebble protocol. Configure: none @@ -24,8 +24,4 @@ Files: - '%{_datadir}/%{name}/qml' - '%{_datadir}/applications/%{name}.desktop' - '%{_datadir}/icons/hicolor/86x86/apps/%{name}.png' -- /usr/bin -- /usr/share/waterwatch -- /usr/share/applications -- /usr/share/icons/hicolor/86x86/apps PkgBR: [] diff --git a/src/watchconnector.cpp b/src/watchconnector.cpp deleted file mode 100644 index c58e867..0000000 --- a/src/watchconnector.cpp +++ /dev/null @@ -1,300 +0,0 @@ -#include "watchconnector.h" -#include - -using namespace watch; - -static int __reconnect_timeout = 1000; - -WatchConnector::WatchConnector(QObject *parent) : - QObject(parent) -{ - socket = nullptr; -} - -WatchConnector::~WatchConnector() -{ -} - -void WatchConnector::deviceDiscovered(const QBluetoothDeviceInfo &device) -{ - //FIXME TODO: Configurable - if (device.name().startsWith("Pebble")) { - qDebug() << "Found Pebble:" << device.name() << '(' << device.address().toString() << ')'; - handleWatch(device); - } else { - qDebug() << "Found other device:" << device.name() << '(' << device.address().toString() << ')'; - } -} - -void WatchConnector::deviceConnect(const QString name, const QString address) -{ - if (name.startsWith("Pebble")) { - _last_name = name; - _last_address = address; - QBluetoothDeviceInfo device(QBluetoothAddress(address), name, 0); - deviceDiscovered(device); - } -} - -void WatchConnector::reconnect() -{ - if (_last_name != "" && _last_address != "") { - deviceConnect(_last_name, _last_address); - } -} - -void WatchConnector::handleWatch(const QBluetoothDeviceInfo &device) -{ - qDebug() << "handleWatch" << device.name(); - if (socket != nullptr) { - socket->close(); - socket->deleteLater(); - socket = nullptr; - } - - socket = new QBluetoothSocket(QBluetoothSocket::RfcommSocket); - qDebug() << "Creating socket"; - - // FIXME: Assuming port 1 (with Pebble) - socket->connectToService(device.address(), 1); - - connect(socket, SIGNAL(readyRead()), this, SLOT(readSocket())); - connect(socket, SIGNAL(connected()), this, SLOT(connected())); - connect(socket, SIGNAL(disconnected()), this, SLOT(disconnected())); -} - -QString WatchConnector::decodeEndpoint(unsigned int val) -{ - //FIXME: Create a map of these values - switch(val) { - case watchTIME: - return "TIME"; - case watchVERSION: - return "VERSION"; - case watchPHONE_VERSION: - return "PHONE_VERSION"; - case watchSYSTEM_MESSAGE: - return "SYSTEM_MESSAGE"; - case watchMUSIC_CONTROL: - return "MUSIC_CONTROL"; - case watchPHONE_CONTROL: - return "PHONE_CONTROL"; - case watchAPPLICATION_MESSAGE: - return "APP_MSG"; - case watchLAUNCHER: - return "LAUNCHER"; - case watchLOGS: - return "LOGS"; - case watchPING: - return "PING"; - case watchLOG_DUMP: - return "DUMP"; - case watchRESET: - return "RESET"; - case watchAPP: - return "APP"; - case watchAPP_LOGS: - return "APP_LOGS"; - case watchNOTIFICATION: - return "NOTIFICATION"; - case watchRESOURCE: - return "RESOURCE"; - case watchAPP_MANAGER: - return "APP_MANAG"; - case watchSCREENSHOT: - return "SCREENSHOT"; - case watchPUTBYTES: - return "PUTBYTES"; - default: - return "Unknown: "+ QString::number(val); - } -} - -void WatchConnector::decodeMsg(QByteArray data) -{ - unsigned int datalen = 0; - int index = 0; - datalen = (data.at(index) << 8) + data.at(index+1); - index += 2; - - unsigned int endpoint = 0; - endpoint = (data.at(index) << 8) + data.at(index+1); - index += 2; - - qDebug() << "Length:" << datalen << " Endpoint:" << decodeEndpoint(endpoint); - qDebug() << "Data:" << data.mid(index).toHex(); - if (endpoint == watchPHONE_CONTROL) { - if (data.length() >= 5) { - if (data.at(4) == callHANGUP) { - emit hangup(); - } - } - } -} - -void WatchConnector::readSocket() -{ - qDebug() << "read"; - - QBluetoothSocket *socket = qobject_cast(sender()); - if (!socket) return; - - while (socket->bytesAvailable()) { - QByteArray line = socket->readAll(); - emit messageReceived(socket->peerName(), QString::fromUtf8(line.constData(), line.length())); - decodeMsg(line); - } -} - -void WatchConnector::connected() -{ - qDebug() << "Connected!"; - is_connected = true; - emit nameChanged(); - emit connectedChanged(); -} - -void WatchConnector::disconnected() -{ - qDebug() << "Disconnected!"; - is_connected = false; - - QBluetoothSocket *socket = qobject_cast(sender()); - if (!socket) return; - - socket->deleteLater(); - socket = nullptr; - emit connectedChanged(); - emit nameChanged(); - - // Try to connect again after a timeout - QTimer::singleShot(__reconnect_timeout, this, SLOT(reconnect())); -} - -void WatchConnector::sendData(const QByteArray &data) -{ - if (socket == nullptr) return; - - socket->write(data); -} - -void WatchConnector::sendMessage(unsigned int endpoint, QByteArray data) -{ - qDebug() << "Sending message"; - QByteArray msg; - - // First send the length - msg.append((data.length() & 0xFF00) >> 8); - msg.append(data.length() & 0xFF); - - // Then the endpoint - msg.append((endpoint & 0xFF00) >> 8); - msg.append(endpoint & 0xFF); - - // Finally the data - msg.append(data); - - sendData(msg); -} - -void WatchConnector::buildData(QByteArray &res, QStringList data) -{ - for (QString d : data) - { - QByteArray tmp = d.left(0xF0).toUtf8(); - res.append(tmp.length() & 0xFF); - res.append(tmp); - } -} - -QByteArray WatchConnector::buildMessageData(unsigned int lead, QStringList data) -{ - QByteArray res; - res.append(lead & 0xFF); - buildData(res, data); - - return res; -} - -void WatchConnector::ping(unsigned int val) -{ - QByteArray res; - res.append((char)0); - - res.append((char)((val >> 24) & 0xff)); - res.append((char)((val >> 16) & 0xff)); - res.append((char)((val >> 8) & 0xff)); - res.append((char)(val & 0xff)); - - sendMessage(watchPING, res); -} - -QString WatchConnector::timeStamp() -{ - return QString::number(QDateTime::currentMSecsSinceEpoch()); -} - -void WatchConnector::sendNotification(unsigned int lead, QString sender, QString data, QString subject) -{ - QStringList tmp; - tmp.append(sender); - tmp.append(data); - tmp.append(timeStamp()); - if (lead == 0) tmp.append(subject); - - QByteArray res = buildMessageData(lead, tmp); - - sendMessage(watchNOTIFICATION, res); -} - -void WatchConnector::sendSMSNotification(QString sender, QString data) -{ - sendNotification(1, sender, data, ""); -} - -void WatchConnector::sendEmailNotification(QString sender, QString data, QString subject) -{ - sendNotification(0, sender, data, subject); -} - -void WatchConnector::phoneControl(char act, unsigned int cookie, QStringList datas) -{ - QByteArray head; - head.append((char)act); - head.append((cookie >> 24)& 0xFF); - head.append((cookie >> 16)& 0xFF); - head.append((cookie >> 8)& 0xFF); - head.append(cookie & 0xFF); - if (datas.length()>0) buildData(head, datas); - - sendMessage(watchPHONE_CONTROL, head); -} - -void WatchConnector::ring(QString number, QString name, bool incoming, unsigned int cookie) -{ - QStringList tmp; - tmp.append(number); - tmp.append(name); - - char act = callINCOMING; - if (!incoming) { - act = callOUTGOING; - } - - phoneControl(act, cookie, tmp); -} - -void WatchConnector::startPhoneCall(unsigned int cookie) -{ - phoneControl(callSTART, cookie, QStringList()); -} - -void WatchConnector::endPhoneCall(unsigned int cookie) -{ - phoneControl(callEND, cookie, QStringList()); -} - -void watch::registerWatchConnector() -{ - qmlRegisterType("watch", 0, 1, "WatchConnector"); -} diff --git a/src/watchconnector.h b/src/watchconnector.h deleted file mode 100644 index 56a0f5c..0000000 --- a/src/watchconnector.h +++ /dev/null @@ -1,133 +0,0 @@ -/* - Copyright (C) 2014 Jouni Roivas - All rights reserved. - - You may use this file under the terms of BSD license as follows: - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of the authors nor the - names of its contributors may be used to endorse or promote products - derived from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR - ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#ifndef WATCHCONNECTOR_H -#define WATCHCONNECTOR_H - -#include -#include -#include -#include -#include -#include - -using namespace QtBluetooth; - -namespace watch -{ - -class WatchConnector : public QObject -{ - Q_OBJECT - Q_PROPERTY(QString name READ name NOTIFY nameChanged) - Q_PROPERTY(QString isConnected READ isConnected NOTIFY connectedChanged) -public: - enum { - 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, - watchRESOURCE = 4000, - watchAPP_MANAGER = 6000, - watchSCREENSHOT = 8000, - watchPUTBYTES = 48879 - }; - enum { - callANSWER = 1, - callHANGUP = 2, - callGET_STATE = 3, - callINCOMING = 4, - callOUTGOING = 5, - callMISSED = 6, - callRING = 7, - callSTART = 8, - callEND = 9 - }; - explicit WatchConnector(QObject *parent = 0); - virtual ~WatchConnector(); - bool isConnected() const { return is_connected; } - QString name() const { if (socket != nullptr) return socket->peerName(); return ""; } - - QString timeStamp(); - QString decodeEndpoint(unsigned int val); - -signals: - void messageReceived(QString peer, QString msg); - void nameChanged(); - void connectedChanged(); - void hangup(); - -public slots: - void sendData(const QByteArray &data); - void sendMessage(unsigned int endpoint, QByteArray data); - void ping(unsigned int val); - void sendNotification(unsigned int lead, QString sender, QString data, QString subject); - void sendSMSNotification(QString sender, QString data); - void sendEmailNotification(QString sender, QString data, QString subject); - - void buildData(QByteArray &res, QStringList data); - QByteArray buildMessageData(unsigned int lead, QStringList data); - - void phoneControl(char act, unsigned int cookie, QStringList datas); - void ring(QString number, QString name, bool incoming=true, unsigned int cookie=0); - void startPhoneCall(unsigned int cookie=0); - void endPhoneCall(unsigned int cookie=0); - - void deviceConnect(const QString name, const QString address); - void deviceDiscovered(const QBluetoothDeviceInfo&); - void handleWatch(const QBluetoothDeviceInfo&); - void readSocket(); - void connected(); - void disconnected(); - void reconnect(); - -private: - void decodeMsg(QByteArray data); - QBluetoothSocket *socket; - bool is_connected; - QString _last_name; - QString _last_address; -}; - -void registerWatchConnector(); - -} - -#endif // WATCHCONNECTOR_H diff --git a/src/waterwatch.cpp b/src/waterwatch.cpp deleted file mode 100644 index 795a310..0000000 --- a/src/waterwatch.cpp +++ /dev/null @@ -1,44 +0,0 @@ -/* - Copyright (C) 2014 Jouni Roivas - Copyright (C) 2013 Jolla Ltd. - Contact: Thomas Perl - All rights reserved. - - You may use this file under the terms of BSD license as follows: - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of the authors nor the - names of its contributors may be used to endorse or promote products - derived from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR - ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#include - -#include -#include "watchconnector.h" - -int main(int argc, char *argv[]) -{ - // Registert WatchController object on QML side - watch::registerWatchConnector(); - - return SailfishApp::main(argc, argv); -} - diff --git a/waterwatch.desktop b/waterwatch.desktop deleted file mode 100644 index 4ddba76..0000000 --- a/waterwatch.desktop +++ /dev/null @@ -1,7 +0,0 @@ -[Desktop Entry] -Type=Application -X-Nemo-Application-Type=silica-qt5 -Name=waterwatch -Icon=waterwatch -Exec=waterwatch - diff --git a/waterwatch.png b/waterwatch.png deleted file mode 100644 index f4aaeeb..0000000 Binary files a/waterwatch.png and /dev/null differ diff --git a/waterwatch.pro b/waterwatch.pro index 65a9018..74f613f 100644 --- a/waterwatch.pro +++ b/waterwatch.pro @@ -1,20 +1,6 @@ -TARGET = waterwatch - -CONFIG += sailfishapp - -SOURCES += src/waterwatch.cpp \ - src/watchconnector.cpp - -QT += bluetooth -QMAKE_CXXFLAGS += -std=c++0x - -OTHER_FILES += qml/waterwatch.qml \ - qml/cover/CoverPage.qml \ +TEMPLATE = subdirs +CONFIG += ordered +SUBDIRS = lib daemon app +OTHER_FILES += \ rpm/waterwatch.spec \ - rpm/waterwatch.yaml \ - waterwatch.desktop \ - qml/pages/WatchPage.qml - -HEADERS += \ - src/watchconnector.h - + rpm/waterwatch.yaml -- cgit v1.2.3