#include #include #include #include #include "controller.h" static const QLatin1String setting_address("address"); static const QLatin1String setting_transport("transport"); static const QLatin1String setting_cur_page("cur-page"); static const QLatin1String setting_24h_mode("24h-mode"); static const QLatin1String setting_ddmm_mode("ddmm-mode"); static const QLatin1String setting_seconds("show-seconds"); static const QLatin1String setting_separation_lines("show-separation-lines"); static const QLatin1String setting_auto_backlight("auto-backlight"); Controller::Controller(const QString &settingsPrefix, QQuickView *view, QObject *parent) : QObject(parent), _settings(new MDConfGroup(this)), _metawatch(0), _reconnect(new ReconnectTimer(this)), _monitor(NotificationMonitor::instance()), _widgets(new WidgetInfoModel(settingsPrefix, this)), _curMode(MetaWatch::WatchModeIdle), _curPage(0), _batteryCharge(0), _batteryCharging(false) { _settings->setPath(settingsPrefix); _curPage = _settings->value(setting_cur_page).toInt(); connect(_settings, &MDConfGroup::valueChanged, this, &Controller::handleSettingChanged); connect(_monitor, &NotificationMonitor::incomingNotification, this, &Controller::handleIncomingNotification); connect(_widgets, &QAbstractItemModel::dataChanged, this, &Controller::handleWidgetChanged); connectToDevice(); } Controller::~Controller() { } bool Controller::isWatchConnected() const { return _metawatch && _metawatch->isDeviceConnected(); } MetaWatch::WatchMode Controller::mode() const { return _curMode; } int Controller::page() const { return _curPage; } void Controller::setPage(int page) { if (page != _curPage) { _curPage = page; if (isWatchConnected()) { _metawatch->updateLcdDisplayPage(_curPage); } emit pageChanged(); } } int Controller::batteryCharge() const { return _batteryCharge; } bool Controller::batteryCharging() const { return _batteryCharging; } void Controller::connectToDevice() { const QString address = _settings->value(setting_address).toString(); const QString transport_name = _settings->value(setting_transport).toString(); MetaWatch::TransportType transport; if (transport_name.compare("bluetooth", Qt::CaseInsensitive) == 0) { qDebug() << "Using Bluetooth (RFCOMM)"; transport = MetaWatch::TransportBluetooth; } else { // Default to BLE qDebug() << "Using Bluetooth Low Energy (GATT)"; transport = MetaWatch::TransportBluetoothLowEnergy; } if (_metawatch) { if (address == _address) { // Connecting to the same address and there's already a watch object created return; } _reconnect->stop(); disconnect(_metawatch, 0, this, 0); disconnect(_metawatch, 0, _reconnect, 0); delete _metawatch; _metawatch = 0; } _address = address; if (_address.isEmpty()) { qDebug() << "Empty address, doing nothing except wasting memory"; return; } _metawatch = new MetaWatch(_address, transport, this); connect(_metawatch, &MetaWatch::connected, _reconnect, &ReconnectTimer::stop); connect(_metawatch, &MetaWatch::connected, this, &Controller::handleMetaWatchConnected); connect(_metawatch, &MetaWatch::disconnected, _reconnect, &ReconnectTimer::scheduleNextAttempt); connect(_metawatch, &MetaWatch::modeChange, this, &Controller::handleMetaWatchModeChange); connect(_metawatch, &MetaWatch::batteryStatus, this, &Controller::handleMetaWatchBatteryStatus); connect(_reconnect, &ReconnectTimer::tryReconnect, _metawatch, &MetaWatch::connectDevice); qDebug() << "Scheduling initial connection attempt to" << _address; _reconnect->scheduleNextAttempt(); } void Controller::updateProperties() { MetaWatch::WatchProperties props; if (!isWatchConnected()) return; if (_settings->value(setting_24h_mode).toBool()) { props |= MetaWatch::WatchPropertyHourFormat24h; } if (_settings->value(setting_ddmm_mode).toBool()) { props |= MetaWatch::WatchPropertyDateFormatDDMM; } if (_settings->value(setting_seconds).toBool()) { props |= MetaWatch::WatchPropertyShowSeconds; } if (_settings->value(setting_separation_lines).toBool()) { props |= MetaWatch::WatchPropertyShowSeparationLines; } if (_settings->value(setting_auto_backlight).toBool()) { props |= MetaWatch::WatchPropertyAutoBacklight; } _metawatch->configure(props); } void Controller::handleSettingChanged(const QString &key) { qDebug() << "Setting" << key << "changed"; if (key == setting_address) { connectToDevice(); } else if (key == setting_cur_page) { int page = _settings->value(setting_cur_page).toInt(); if (_curPage != page) { setPage(page); } } else if (key == setting_24h_mode || key == setting_ddmm_mode || key == setting_seconds || key == setting_separation_lines || key == setting_auto_backlight) { updateProperties(); } } void Controller::handleMetaWatchConnected() { qDebug() << "MetaWatch connected, synchronizing date time"; _metawatch->updateDeviceType(); _metawatch->updateBatteryStatus(); _metawatch->setDateTime(QDateTime::currentDateTime()); _metawatch->updateWidgetList(_widgets->toList()); _metawatch->updateLcdDisplayPage(_curPage); _curMode = MetaWatch::WatchModeIdle; updateProperties(); } void Controller::handleMetaWatchModeChange(MetaWatch::WatchMode mode, int page) { switch (mode) { case MetaWatch::WatchModeIdle: if (_curMode != mode) { _curMode = mode; emit modeChanged(); } if (page != _curPage) { _curPage = page; _settings->setValue(setting_cur_page, QVariant::fromValue(_curPage)); emit pageChanged(); } break; case MetaWatch::WatchModeNotification: if (_curMode != mode) { _curMode = mode; emit modeChanged(); } // Watch will often go back to the same page once it returns to idle mode, // so let's not clobber _curPage. break; default: qWarning() << "Unhandled metawatch mode:" << mode; break; } } void Controller::handleMetaWatchBatteryStatus(bool charging, int charge) { if (_batteryCharging != charging) { _batteryCharging = charging; emit batteryChargingChanged(); } if (_batteryCharge != charge) { _batteryCharge = charge; emit batteryChargeChanged(); } } void Controller::handleIncomingNotification(const QString &sender, const QIcon &icon, const QString &summary, int count, const QString &body, const QDateTime &dateTime) { if (!isWatchConnected()) { return; // Ignoring notification if metawatch is not connected. } QImage image(96, 96, QImage::Format_MonoLSB); QPainter p(&image); QFont small("MetaWatch Small caps 8pt"); small.setPixelSize(8); QFont large("MetaWatch Large 16pt"); large.setPixelSize(16); // Render notification background p.drawImage(0, 0, QImage(SailfishApp::pathTo("qml/watch/notification.png").toLocalFile())); // Render the associated icon on the bottom left corner icon.paint(&p, 0, 96 - 24, 28, 24, Qt::AlignLeft | Qt::AlignBottom); QString title = summary; bool title_drawn_fully = false; if (count > 1) { title = QString("%1 %2").arg(count).arg(summary); } // Render title area p.setFont(large); if (!title.isEmpty()) { QRect area(2, 2, 96 - 18, 18); QRect r; p.drawText(area, Qt::AlignLeft | Qt::AlignTop | Qt::TextSingleLine, title, &r); if (area.contains(r)) { title_drawn_fully = true; } } const int x = 2, max_x = 96; const int max_y = 96 - 24; int y = 26; // Now render the contents // If the title did not fit in the title area, also draw it fully here. p.setFont(small); if (!title_drawn_fully && !title.isEmpty()) { QRect r; p.drawText(QRect(x, y, max_x - x, max_y - y), Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, title, &r); y = r.bottom() + 1; } if (!body.isEmpty() && y < max_y) { QRect r; p.drawText(QRect(x, y, max_x - x, max_y - y), Qt::AlignLeft | Qt::AlignTop | Qt::TextWordWrap, body, &r); y = r.bottom() + 1; } image.invertPixels(); _metawatch->clearModeImage(MetaWatch::WatchModeNotification); _metawatch->sendModeImage(MetaWatch::WatchModeNotification, image); _metawatch->updateLcdDisplayMode(MetaWatch::WatchModeNotification); _metawatch->setVibrateMode(true, 500, 500, 2); } void Controller::handleWidgetChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector &roles) { if (!isWatchConnected()) return; if (roles.indexOf(WidgetInfoModel::UrlRole) >= 0) { // Can't send partial updates to watch, so entire list must be // resubmitted everytime something important changes _metawatch->updateWidgetList(_widgets->toList()); // If the changed widget is on the current page, watch needs to refresh. if (_widgets->data(topLeft, WidgetInfoModel::PageRole).toInt() == _curPage) { _metawatch->updateLcdDisplayPage(_curPage); } } }