summaryrefslogtreecommitdiff
path: root/rockworkd/libpebble/pebble.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'rockworkd/libpebble/pebble.cpp')
-rw-r--r--rockworkd/libpebble/pebble.cpp693
1 files changed, 693 insertions, 0 deletions
diff --git a/rockworkd/libpebble/pebble.cpp b/rockworkd/libpebble/pebble.cpp
new file mode 100644
index 0000000..5655cc7
--- /dev/null
+++ b/rockworkd/libpebble/pebble.cpp
@@ -0,0 +1,693 @@
+#include "pebble.h"
+#include "watchconnection.h"
+#include "notificationendpoint.h"
+#include "watchdatareader.h"
+#include "watchdatawriter.h"
+#include "musicendpoint.h"
+#include "phonecallendpoint.h"
+#include "appmanager.h"
+#include "appmsgmanager.h"
+#include "jskit/jskitmanager.h"
+#include "blobdb.h"
+#include "appdownloader.h"
+#include "screenshotendpoint.h"
+#include "firmwaredownloader.h"
+#include "watchlogendpoint.h"
+#include "core.h"
+#include "platforminterface.h"
+#include "ziphelper.h"
+#include "dataloggingendpoint.h"
+
+#include "QDir"
+#include <QDateTime>
+#include <QStandardPaths>
+#include <QSettings>
+#include <QTimeZone>
+
+Pebble::Pebble(const QBluetoothAddress &address, QObject *parent):
+ QObject(parent),
+ m_address(address)
+{
+ m_storagePath = QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/" + m_address.toString().replace(':', '_') + "/";
+
+ m_connection = new WatchConnection(this);
+ QObject::connect(m_connection, &WatchConnection::watchConnected, this, &Pebble::onPebbleConnected);
+ QObject::connect(m_connection, &WatchConnection::watchDisconnected, this, &Pebble::onPebbleDisconnected);
+
+ m_connection->registerEndpointHandler(WatchConnection::EndpointVersion, this, "pebbleVersionReceived");
+ m_connection->registerEndpointHandler(WatchConnection::EndpointPhoneVersion, this, "phoneVersionAsked");
+ m_connection->registerEndpointHandler(WatchConnection::EndpointFactorySettings, this, "factorySettingsReceived");
+
+ m_dataLogEndpoint = new DataLoggingEndpoint(this, m_connection);
+
+ m_notificationEndpoint = new NotificationEndpoint(this, m_connection);
+ QObject::connect(Core::instance()->platform(), &PlatformInterface::notificationReceived, this, &Pebble::sendNotification);
+
+ m_musicEndpoint = new MusicEndpoint(this, m_connection);
+ m_musicEndpoint->setMusicMetadata(Core::instance()->platform()->musicMetaData());
+ QObject::connect(m_musicEndpoint, &MusicEndpoint::musicControlPressed, Core::instance()->platform(), &PlatformInterface::sendMusicControlCommand);
+ QObject::connect(Core::instance()->platform(), &PlatformInterface::musicMetadataChanged, m_musicEndpoint, &MusicEndpoint::setMusicMetadata);
+
+ m_phoneCallEndpoint = new PhoneCallEndpoint(this, m_connection);
+ QObject::connect(m_phoneCallEndpoint, &PhoneCallEndpoint::hangupCall, Core::instance()->platform(), &PlatformInterface::hangupCall);
+ QObject::connect(Core::instance()->platform(), &PlatformInterface::incomingCall, m_phoneCallEndpoint, &PhoneCallEndpoint::incomingCall);
+ QObject::connect(Core::instance()->platform(), &PlatformInterface::callStarted, m_phoneCallEndpoint, &PhoneCallEndpoint::callStarted);
+ QObject::connect(Core::instance()->platform(), &PlatformInterface::callEnded, m_phoneCallEndpoint, &PhoneCallEndpoint::callEnded);
+
+ m_appManager = new AppManager(this, m_connection);
+ QObject::connect(m_appManager, &AppManager::appsChanged, this, &Pebble::installedAppsChanged);
+ QObject::connect(m_appManager, &AppManager::idMismatchDetected, this, &Pebble::resetPebble);
+
+ m_appMsgManager = new AppMsgManager(this, m_appManager, m_connection);
+ m_jskitManager = new JSKitManager(this, m_connection, m_appManager, m_appMsgManager, this);
+ QObject::connect(m_jskitManager, SIGNAL(openURL(const QString&, const QString&)), this, SIGNAL(openURL(const QString&, const QString&)));
+
+ m_blobDB = new BlobDB(this, m_connection);
+ QObject::connect(m_blobDB, &BlobDB::muteSource, this, &Pebble::muteNotificationSource);
+ QObject::connect(m_blobDB, &BlobDB::actionTriggered, Core::instance()->platform(), &PlatformInterface::actionTriggered);
+ QObject::connect(m_blobDB, &BlobDB::appInserted, this, &Pebble::appInstalled);
+ QObject::connect(Core::instance()->platform(), &PlatformInterface::organizerItemsChanged, this, &Pebble::syncCalendar);
+
+ m_appDownloader = new AppDownloader(m_storagePath, this);
+ QObject::connect(m_appDownloader, &AppDownloader::downloadFinished, this, &Pebble::appDownloadFinished);
+
+ m_screenshotEndpoint = new ScreenshotEndpoint(this, m_connection, this);
+ QObject::connect(m_screenshotEndpoint, &ScreenshotEndpoint::screenshotAdded, this, &Pebble::screenshotAdded);
+ QObject::connect(m_screenshotEndpoint, &ScreenshotEndpoint::screenshotRemoved, this, &Pebble::screenshotRemoved);
+
+ m_firmwareDownloader = new FirmwareDownloader(this, m_connection);
+ QObject::connect(m_firmwareDownloader, &FirmwareDownloader::updateAvailableChanged, this, &Pebble::slotUpdateAvailableChanged);
+ QObject::connect(m_firmwareDownloader, &FirmwareDownloader::upgradingChanged, this, &Pebble::upgradingFirmwareChanged);
+
+ m_logEndpoint = new WatchLogEndpoint(this, m_connection);
+ QObject::connect(m_logEndpoint, &WatchLogEndpoint::logsFetched, this, &Pebble::logsDumped);
+
+ QSettings watchInfo(m_storagePath + "/watchinfo.conf", QSettings::IniFormat);
+ m_model = (Model)watchInfo.value("watchModel", (int)ModelUnknown).toInt();
+
+ QSettings settings(m_storagePath + "/appsettings.conf", QSettings::IniFormat);
+ settings.beginGroup("activityParams");
+ m_healthParams.setEnabled(settings.value("enabled").toBool());
+ m_healthParams.setAge(settings.value("age").toUInt());
+ m_healthParams.setHeight(settings.value("height").toInt());
+ m_healthParams.setGender((HealthParams::Gender)settings.value("gender").toInt());
+ m_healthParams.setWeight(settings.value("weight").toInt());
+ m_healthParams.setMoreActive(settings.value("moreActive").toBool());
+ m_healthParams.setSleepMore(settings.value("sleepMore").toBool());
+ settings.endGroup();
+
+ settings.beginGroup("unitsDistance");
+ m_imperialUnits = settings.value("imperialUnits", false).toBool();
+ settings.endGroup();
+
+ settings.beginGroup("calendar");
+ m_calendarSyncEnabled = settings.value("calendarSyncEnabled", true).toBool();
+ settings.endGroup();
+}
+
+QBluetoothAddress Pebble::address() const
+{
+ return m_address;
+}
+
+QString Pebble::name() const
+{
+ return m_name;
+}
+
+void Pebble::setName(const QString &name)
+{
+ m_name = name;
+}
+
+QBluetoothLocalDevice::Pairing Pebble::pairingStatus() const
+{
+ QBluetoothLocalDevice dev;
+ return dev.pairingStatus(m_address);
+}
+
+bool Pebble::connected() const
+{
+ return m_connection->isConnected() && !m_serialNumber.isEmpty();
+}
+
+void Pebble::connect()
+{
+ qDebug() << "Connecting to Pebble:" << m_name << m_address;
+ m_connection->connectPebble(m_address);
+}
+
+QDateTime Pebble::softwareBuildTime() const
+{
+ return m_softwareBuildTime;
+}
+
+QString Pebble::softwareVersion() const
+{
+ return m_softwareVersion;
+}
+
+QString Pebble::softwareCommitRevision() const
+{
+ return m_softwareCommitRevision;
+}
+
+HardwareRevision Pebble::hardwareRevision() const
+{
+ return m_hardwareRevision;
+}
+
+Model Pebble::model() const
+{
+ return m_model;
+}
+
+void Pebble::setHardwareRevision(HardwareRevision hardwareRevision)
+{
+ m_hardwareRevision = hardwareRevision;
+ switch (m_hardwareRevision) {
+ case HardwareRevisionUNKNOWN:
+ m_hardwarePlatform = HardwarePlatformUnknown;
+ break;
+ case HardwareRevisionTINTIN_EV1:
+ case HardwareRevisionTINTIN_EV2:
+ case HardwareRevisionTINTIN_EV2_3:
+ case HardwareRevisionTINTIN_EV2_4:
+ case HardwareRevisionTINTIN_V1_5:
+ case HardwareRevisionBIANCA:
+ case HardwareRevisionTINTIN_BB:
+ case HardwareRevisionTINTIN_BB2:
+ m_hardwarePlatform = HardwarePlatformAplite;
+ break;
+ case HardwareRevisionSNOWY_EVT2:
+ case HardwareRevisionSNOWY_DVT:
+ case HardwareRevisionBOBBY_SMILES:
+ case HardwareRevisionSNOWY_BB:
+ case HardwareRevisionSNOWY_BB2:
+ m_hardwarePlatform = HardwarePlatformBasalt;
+ break;
+ case HardwareRevisionSPALDING_EVT:
+ case HardwareRevisionSPALDING:
+ case HardwareRevisionSPALDING_BB2:
+ m_hardwarePlatform = HardwarePlatformChalk;
+ break;
+ }
+}
+
+HardwarePlatform Pebble::hardwarePlatform() const
+{
+ return m_hardwarePlatform;
+}
+
+QString Pebble::serialNumber() const
+{
+ return m_serialNumber;
+}
+
+QString Pebble::language() const
+{
+ return m_language;
+}
+
+Capabilities Pebble::capabilities() const
+{
+ return m_capabilities;
+}
+
+bool Pebble::isUnfaithful() const
+{
+ return m_isUnfaithful;
+}
+
+bool Pebble::recovery() const
+{
+ return m_recovery;
+}
+
+bool Pebble::upgradingFirmware() const
+{
+ return m_firmwareDownloader->upgrading();
+}
+
+void Pebble::setHealthParams(const HealthParams &healthParams)
+{
+ m_healthParams = healthParams;
+ m_blobDB->setHealthParams(healthParams);
+ emit healtParamsChanged();
+
+ QSettings healthSettings(m_storagePath + "/appsettings.conf", QSettings::IniFormat);
+ healthSettings.beginGroup("activityParams");
+ healthSettings.setValue("enabled", m_healthParams.enabled());
+ healthSettings.setValue("age", m_healthParams.age());
+ healthSettings.setValue("height", m_healthParams.height());
+ healthSettings.setValue("gender", m_healthParams.gender());
+ healthSettings.setValue("weight", m_healthParams.weight());
+ healthSettings.setValue("moreActive", m_healthParams.moreActive());
+ healthSettings.setValue("sleepMore", m_healthParams.sleepMore());
+
+}
+
+HealthParams Pebble::healthParams() const
+{
+ return m_healthParams;
+}
+
+void Pebble::setImperialUnits(bool imperial)
+{
+ m_imperialUnits = imperial;
+ m_blobDB->setUnits(imperial);
+ emit imperialUnitsChanged();
+
+ QSettings settings(m_storagePath + "/appsettings.conf", QSettings::IniFormat);
+ settings.beginGroup("unitsDistance");
+ settings.setValue("enabled", m_imperialUnits);
+}
+
+bool Pebble::imperialUnits() const
+{
+ return m_imperialUnits;
+}
+
+void Pebble::dumpLogs(const QString &fileName) const
+{
+ m_logEndpoint->fetchLogs(fileName);
+}
+
+QString Pebble::storagePath() const
+{
+ return m_storagePath;
+}
+
+QHash<QString, bool> Pebble::notificationsFilter() const
+{
+ QHash<QString, bool> ret;
+ QString settingsFile = m_storagePath + "/notifications.conf";
+ QSettings s(settingsFile, QSettings::IniFormat);
+ foreach (const QString &key, s.allKeys()) {
+ ret.insert(key, s.value(key).toBool());
+ }
+ return ret;
+}
+
+void Pebble::setNotificationFilter(const QString &sourceId, bool enabled)
+{
+ QString settingsFile = m_storagePath + "/notifications.conf";
+ QSettings s(settingsFile, QSettings::IniFormat);
+ if (!s.contains(sourceId) || s.value(sourceId).toBool() != enabled) {
+ s.setValue(sourceId, enabled);
+ emit notificationFilterChanged(sourceId, enabled);
+ }
+}
+
+void Pebble::sendNotification(const Notification &notification)
+{
+ if (!notificationsFilter().value(notification.sourceId(), true)) {
+ qDebug() << "Notifications for" << notification.sourceId() << "disabled.";
+ return;
+ }
+ // In case it wasn't there before, make sure to write it to the config now so it will appear in the config app.
+ setNotificationFilter(notification.sourceId(), true);
+
+ qDebug() << "Sending notification from source" << notification.sourceId() << "to watch";
+
+ if (m_softwareVersion < "v3.0") {
+ m_notificationEndpoint->sendLegacyNotification(notification);
+ } else {
+ m_blobDB->insertNotification(notification);
+ }
+}
+
+void Pebble::clearAppDB()
+{
+ m_blobDB->clearApps();
+}
+
+void Pebble::clearTimeline()
+{
+ m_blobDB->clearTimeline();
+}
+
+void Pebble::setCalendarSyncEnabled(bool enabled)
+{
+ if (m_calendarSyncEnabled == enabled) {
+ return;
+ }
+ m_calendarSyncEnabled = enabled;
+ emit calendarSyncEnabledChanged();
+
+ if (!m_calendarSyncEnabled) {
+ m_blobDB->clearTimeline();
+ } else {
+ syncCalendar(Core::instance()->platform()->organizerItems());
+ }
+
+ QSettings settings(m_storagePath + "/appsettings.conf", QSettings::IniFormat);
+ settings.beginGroup("calendar");
+ settings.setValue("calendarSyncEnabled", m_calendarSyncEnabled);
+ settings.endGroup();
+}
+
+bool Pebble::calendarSyncEnabled() const
+{
+ return m_calendarSyncEnabled;
+}
+
+void Pebble::syncCalendar(const QList<CalendarEvent> &items)
+{
+ if (connected() && m_calendarSyncEnabled) {
+ m_blobDB->syncCalendar(items);
+ }
+}
+
+void Pebble::installApp(const QString &id)
+{
+ m_appDownloader->downloadApp(id);
+}
+
+void Pebble::sideloadApp(const QString &packageFile)
+{
+ QString targetFile = packageFile;
+ targetFile.remove("file://");
+
+ QString id;
+ int i = 0;
+ do {
+ QDir dir(m_storagePath + "/apps/sideload" + QString::number(i));
+ if (!dir.exists()) {
+ if (!dir.mkpath(dir.absolutePath())) {
+ qWarning() << "Error creating dir for unpacking. Cannot install package" << packageFile;
+ return;
+ }
+ id = "sideload" + QString::number(i);
+ }
+ i++;
+ } while (id.isEmpty());
+
+ if (!ZipHelper::unpackArchive(targetFile, m_storagePath + "/apps/" + id)) {
+ qWarning() << "Error unpacking App zip file" << targetFile << "to" << m_storagePath + "/apps/" + id;
+ return;
+ }
+
+ qDebug() << "Sideload package unpacked.";
+ appDownloadFinished(id);
+}
+
+QList<QUuid> Pebble::installedAppIds()
+{
+ return m_appManager->appUuids();
+}
+
+void Pebble::setAppOrder(const QList<QUuid> &newList)
+{
+ m_appManager->setAppOrder(newList);
+}
+
+AppInfo Pebble::appInfo(const QUuid &uuid)
+{
+ return m_appManager->info(uuid);
+}
+
+void Pebble::removeApp(const QUuid &uuid)
+{
+ qDebug() << "Should remove app:" << uuid;
+ m_blobDB->removeApp(m_appManager->info(uuid));
+ m_appManager->removeApp(uuid);
+}
+
+void Pebble::launchApp(const QUuid &uuid)
+{
+ m_appMsgManager->launchApp(uuid);
+}
+
+void Pebble::requestConfigurationURL(const QUuid &uuid) {
+ if (m_jskitManager->currentApp().uuid() == uuid) {
+ m_jskitManager->showConfiguration();
+ }
+ else {
+ m_jskitManager->setConfigurationId(uuid);
+ m_appMsgManager->launchApp(uuid);
+ }
+}
+
+void Pebble::configurationClosed(const QUuid &uuid, const QString &result)
+{
+ if (m_jskitManager->currentApp().uuid() == uuid) {
+ m_jskitManager->handleWebviewClosed(result);
+ }
+}
+
+void Pebble::requestScreenshot()
+{
+ m_screenshotEndpoint->requestScreenshot();
+}
+
+QStringList Pebble::screenshots() const
+{
+ return m_screenshotEndpoint->screenshots();
+}
+
+void Pebble::removeScreenshot(const QString &filename)
+{
+ m_screenshotEndpoint->removeScreenshot(filename);
+}
+
+bool Pebble::firmwareUpdateAvailable() const
+{
+ return m_firmwareDownloader->updateAvailable();
+}
+
+QString Pebble::candidateFirmwareVersion() const
+{
+ return m_firmwareDownloader->candidateVersion();
+}
+
+QString Pebble::firmwareReleaseNotes() const
+{
+ return m_firmwareDownloader->releaseNotes();
+}
+
+void Pebble::upgradeFirmware() const
+{
+ m_firmwareDownloader->performUpgrade();
+}
+
+void Pebble::onPebbleConnected()
+{
+ qDebug() << "Pebble connected:" << m_name;
+ QByteArray data;
+ WatchDataWriter w(&data);
+ w.write<quint8>(0); // Command fetch
+ QString message = "mfg_color";
+ w.writeLE<quint8>(message.length());
+ w.writeFixedString(message.length(), message);
+ m_connection->writeToPebble(WatchConnection::EndpointFactorySettings, data);
+
+ m_connection->writeToPebble(WatchConnection::EndpointVersion, QByteArray(1, 0));
+}
+
+void Pebble::onPebbleDisconnected()
+{
+ qDebug() << "Pebble disconnected:" << m_name;
+ emit pebbleDisconnected();
+}
+
+void Pebble::pebbleVersionReceived(const QByteArray &data)
+{
+ WatchDataReader wd(data);
+
+ wd.skip(1);
+ m_softwareBuildTime = QDateTime::fromTime_t(wd.read<quint32>());
+ qDebug() << "Software Version build:" << m_softwareBuildTime;
+ m_softwareVersion = wd.readFixedString(32);
+ qDebug() << "Software Version string:" << m_softwareVersion;
+ m_softwareCommitRevision = wd.readFixedString(8);
+ qDebug() << "Software Version commit:" << m_softwareCommitRevision;
+
+ m_recovery = wd.read<quint8>();
+ qDebug() << "Recovery:" << m_recovery;
+ HardwareRevision rev = (HardwareRevision)wd.read<quint8>();
+ setHardwareRevision(rev);
+ qDebug() << "HW Revision:" << rev;
+ qDebug() << "Metadata Version:" << wd.read<quint8>();
+
+ qDebug() << "Safe build:" << QDateTime::fromTime_t(wd.read<quint32>());
+ qDebug() << "Safe version:" << wd.readFixedString(32);
+ qDebug() << "safe commit:" << wd.readFixedString(8);
+ qDebug() << "Safe recovery:" << wd.read<quint8>();
+ qDebug() << "HW Revision:" << wd.read<quint8>();
+ qDebug() << "Metadata Version:" << wd.read<quint8>();
+
+ qDebug() << "BootloaderBuild" << QDateTime::fromTime_t(wd.read<quint32>());
+ qDebug() << "hardwareRevision" << wd.readFixedString(9);
+ m_serialNumber = wd.readFixedString(12);
+ qDebug() << "serialnumber" << m_serialNumber;
+ qDebug() << "BT address" << wd.readBytes(6).toHex();
+ qDebug() << "CRC:" << wd.read<quint32>();
+ qDebug() << "Resource timestamp:" << QDateTime::fromTime_t(wd.read<quint32>());
+ m_language = wd.readFixedString(6);
+ qDebug() << "Language" << m_language;
+ qDebug() << "Language version" << wd.read<quint16>();
+ // Capabilities is 64 bits but QFlags can only do 32 bits. lets split it into 2 * 32.
+ // only 8 bits are used atm anyways.
+ m_capabilities = QFlag(wd.readLE<quint32>());
+ qDebug() << "Capabilities" << QString::number(m_capabilities, 16);
+ qDebug() << "Capabilities" << wd.readLE<quint32>();
+ m_isUnfaithful = wd.read<quint8>();
+ qDebug() << "Is Unfaithful" << m_isUnfaithful;
+
+ // This is useful for debugging
+ //m_isUnfaithful = true;
+
+ if (!m_recovery) {
+ m_appManager->rescan();
+
+ QSettings version(m_storagePath + "/watchinfo.conf", QSettings::IniFormat);
+ if (version.value("syncedWithVersion").toString() != QStringLiteral(VERSION)) {
+ m_isUnfaithful = true;
+ }
+
+ if (m_isUnfaithful) {
+ qDebug() << "Pebble sync state unclear. Resetting Pebble watch.";
+ resetPebble();
+ } else {
+ syncCalendar(Core::instance()->platform()->organizerItems());
+ syncApps();
+ m_blobDB->setHealthParams(m_healthParams);
+ m_blobDB->setUnits(m_imperialUnits);
+ }
+ version.setValue("syncedWithVersion", QStringLiteral(VERSION));
+
+ syncTime();
+ }
+
+ m_firmwareDownloader->checkForNewFirmware();
+ emit pebbleConnected();
+
+}
+
+void Pebble::factorySettingsReceived(const QByteArray &data)
+{
+ qDebug() << "have factory settings" << data.toHex();
+
+ WatchDataReader reader(data);
+ quint8 status = reader.read<quint8>();
+ quint8 len = reader.read<quint8>();
+
+ if (status != 0x01 && len != 0x04) {
+ qWarning() << "Unexpected data reading factory settings";
+ return;
+ }
+ m_model = (Model)reader.read<quint32>();
+ QSettings s(m_storagePath + "/watchinfo.conf", QSettings::IniFormat);
+ s.setValue("watchModel", m_model);
+}
+
+void Pebble::phoneVersionAsked(const QByteArray &data)
+{
+
+ QByteArray res;
+
+ Capabilities sessionCap(CapabilityHealth
+ | CapabilityAppRunState
+ | CapabilityUpdatedMusicProtocol | CapabilityInfiniteLogDumping | Capability8kAppMessages);
+
+ quint32 platformFlags = 16 | 32 | OSAndroid;
+
+ WatchDataWriter writer(&res);
+ writer.writeLE<quint8>(0x01); // ok
+ writer.writeLE<quint32>(0xFFFFFFFF);
+ writer.writeLE<quint32>(sessionCap);
+ writer.write<quint32>(platformFlags);
+ writer.write<quint8>(2); // response version
+ writer.write<quint8>(3); // major version
+ writer.write<quint8>(0); // minor version
+ writer.write<quint8>(0); // bugfix version
+ writer.writeLE<quint64>(sessionCap);
+
+ qDebug() << "sending phone version" << res.toHex();
+
+ m_connection->writeToPebble(WatchConnection::EndpointPhoneVersion, res);
+}
+
+void Pebble::appDownloadFinished(const QString &id)
+{
+ QUuid uuid = m_appManager->scanApp(m_storagePath + "/apps/" + id);
+ if (uuid.isNull()) {
+ qWarning() << "Error scanning downloaded app. Won't install on watch";
+ return;
+ }
+ m_blobDB->insertAppMetaData(m_appManager->info(uuid));
+ m_pendingInstallations.append(uuid);
+}
+
+void Pebble::appInstalled(const QUuid &uuid) {
+ if (m_pendingInstallations.contains(uuid)) {
+ m_appMsgManager->launchApp(uuid);
+ }
+}
+
+void Pebble::muteNotificationSource(const QString &source)
+{
+ setNotificationFilter(source, false);
+}
+
+void Pebble::resetPebble()
+{
+ clearTimeline();
+ syncCalendar(Core::instance()->platform()->organizerItems());
+
+ clearAppDB();
+ syncApps();
+}
+
+void Pebble::syncApps()
+{
+ foreach (const QUuid &appUuid, m_appManager->appUuids()) {
+ if (!m_appManager->info(appUuid).isSystemApp()) {
+ qDebug() << "Inserting app" << m_appManager->info(appUuid).shortName() << "into BlobDB";
+ m_blobDB->insertAppMetaData(m_appManager->info(appUuid));
+ }
+ }
+ // make sure the order is synced too
+ m_appManager->setAppOrder(m_appManager->appUuids());
+}
+
+void Pebble::syncTime()
+{
+ TimeMessage msg(TimeMessage::TimeOperationSetUTC);
+ qDebug() << "Syncing Time" << QDateTime::currentDateTime() << msg.serialize().toHex();
+ m_connection->writeToPebble(WatchConnection::EndpointTime, msg.serialize());
+}
+
+void Pebble::slotUpdateAvailableChanged()
+{
+ qDebug() << "update available" << m_firmwareDownloader->updateAvailable() << m_firmwareDownloader->candidateVersion();
+
+ emit updateAvailableChanged();
+}
+
+
+TimeMessage::TimeMessage(TimeMessage::TimeOperation operation) :
+ m_operation(operation)
+{
+
+}
+QByteArray TimeMessage::serialize() const
+{
+ QByteArray ret;
+ WatchDataWriter writer(&ret);
+ writer.write<quint8>(m_operation);
+ switch (m_operation) {
+ case TimeOperationSetLocaltime:
+ writer.writeLE<quint32>(QDateTime::currentMSecsSinceEpoch() / 1000);
+ break;
+ case TimeOperationSetUTC:
+ writer.write<quint32>(QDateTime::currentDateTime().toMSecsSinceEpoch() / 1000);
+ writer.write<qint16>(QDateTime::currentDateTime().offsetFromUtc() / 60);
+ writer.writePascalString(QDateTime::currentDateTime().timeZone().displayName(QTimeZone::StandardTime));
+ break;
+ default:
+ ;
+ }
+ return ret;
+}