diff options
Diffstat (limited to 'rockworkd/platformintegration/sailfish/sailfishplatform.cpp')
| -rw-r--r-- | rockworkd/platformintegration/sailfish/sailfishplatform.cpp | 313 |
1 files changed, 313 insertions, 0 deletions
diff --git a/rockworkd/platformintegration/sailfish/sailfishplatform.cpp b/rockworkd/platformintegration/sailfish/sailfishplatform.cpp new file mode 100644 index 0000000..09e1426 --- /dev/null +++ b/rockworkd/platformintegration/sailfish/sailfishplatform.cpp @@ -0,0 +1,313 @@ +#include "sailfishplatform.h" + +#include "callchannelobserver.h" +#include "organizeradapter.h" +#include "syncmonitorclient.h" + +#include <QDBusConnection> +#include <QDBusConnectionInterface> +#include <QDebug> + +SailfishPlatform::SailfishPlatform(QObject *parent): + PlatformInterface(parent), + _pulseBus(NULL), + _maxVolume(0) +{ + // Notifications + QDBusConnection::sessionBus().registerObject("/org/freedesktop/Notifications", this, QDBusConnection::ExportAllSlots); + m_iface = new QDBusInterface("org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus"); + m_iface->call("AddMatch", "interface='org.freedesktop.Notifications',member='Notify',type='method_call',eavesdrop='true'"); + m_iface->call("AddMatch", "interface='org.freedesktop.Notifications',member='CloseNotification',type='method_call',eavesdrop='true'"); + + // Music + QDBusConnectionInterface *iface = QDBusConnection::sessionBus().interface(); + const QStringList &services = iface->registeredServiceNames(); + foreach (QString service, services) { + if (service.startsWith("org.mpris.MediaPlayer2.")) { + qDebug() << "have mpris service" << service; + m_mprisService = service; + fetchMusicMetadata(); + QDBusConnection::sessionBus().connect(m_mprisService, "/org/mpris/MediaPlayer2", "", "PropertiesChanged", this, SLOT(mediaPropertiesChanged(QString,QVariantMap,QStringList))); + break; + } + } + + QDBusMessage call = QDBusMessage::createMethodCall("org.PulseAudio1", "/org/pulseaudio/server_lookup1", "org.freedesktop.DBus.Properties", "Get" ); + call << "org.PulseAudio.ServerLookup1" << "Address"; + QDBusReply<QDBusVariant> lookupReply = QDBusConnection::sessionBus().call(call); + if (lookupReply.isValid()) { + // + qDebug() << "PulseAudio Bus address: " << lookupReply.value().variant().toString(); + _pulseBus = new QDBusConnection(QDBusConnection::connectToPeer(lookupReply.value().variant().toString(), "org.PulseAudio1")); + } + // Query max volume + call = QDBusMessage::createMethodCall("com.Meego.MainVolume2", "/com/meego/mainvolume2", + "org.freedesktop.DBus.Properties", "Get"); + call << "com.Meego.MainVolume2" << "StepCount"; + QDBusReply<QDBusVariant> volumeMaxReply = _pulseBus->call(call); + if (volumeMaxReply.isValid()) { + _maxVolume = volumeMaxReply.value().variant().toUInt(); + qDebug() << "Max volume: " << _maxVolume; + } + else { + qWarning() << "Could not read volume max, cannot adjust volume: " << volumeMaxReply.error().message(); + } + + // Calls + m_telepathyMonitor = new TelepathyMonitor(this); + connect(m_telepathyMonitor, &TelepathyMonitor::incomingCall, this, &SailfishPlatform::incomingCall); + connect(m_telepathyMonitor, &TelepathyMonitor::callStarted, this, &SailfishPlatform::callStarted); + connect(m_telepathyMonitor, &TelepathyMonitor::callEnded, this, &SailfishPlatform::callEnded); + + // Organizer + m_organizerAdapter = new OrganizerAdapter(this); + m_organizerAdapter->refresh(); + connect(m_organizerAdapter, &OrganizerAdapter::itemsChanged, this, &SailfishPlatform::organizerItemsChanged); + m_syncMonitorClient = new SyncMonitorClient(this); + connect(m_syncMonitorClient, &SyncMonitorClient::stateChanged, [this]() { if (m_syncMonitorClient->state() == "idle") m_organizerAdapter->refresh();}); + m_syncTimer.start(1000 * 60 * 60); + connect(&m_syncTimer, &QTimer::timeout, [this]() { m_syncMonitorClient->sync({"calendar"});}); + m_syncMonitorClient->sync({"calendar"}); +} + +QDBusInterface *SailfishPlatform::interface() const +{ + return m_iface; +} + +uint SailfishPlatform::Notify(const QString &app_name, uint replaces_id, const QString &app_icon, const QString &summary, const QString &body, const QStringList &actions, const QVariantHash &hints, int expire_timeout) +{ + qDebug() << "Notification received" << app_name << replaces_id << app_icon << summary << body << actions << hints << expire_timeout; + QString owner = hints.value("x-nemo-owner", "").toString(); + + // Look up the notification category and its parameters + QString category = hints.value("category", "").toString(); + QHash<QString, QString> categoryParams = this->getCategoryParams(category); + + // Ignore transient and hidden 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 (hints.value("transient", categoryParams.value("transient", "false")).toString() == "true") { + qDebug() << "Ignoring transient notification from " << owner; + return 0; + } + else if (hints.value("x-nemo-hidden", "false").toString() == "true" ) { + qDebug() << "Ignoring hidden notification from " << owner; + return 0; + } + + Notification n(app_name); + if (owner == "twitter-notifications-client") { + n.setType(Notification::NotificationTypeTwitter); + n.setSourceName("Twitter"); + } else if (category == "x-nemo.email") { + if (app_name.toLower().contains("gmail")) { + n.setType(Notification::NotificationTypeGMail); + n.setSourceName("GMail"); + } + else { + n.setType(Notification::NotificationTypeEmail); + n.setSubject(app_name); + } + } else if (owner == "facebook-notifications-client") { + n.setType(Notification::NotificationTypeFacebook); + n.setSourceName("Facebook"); + } else if (hints.value("x-nemo-origin-package").toString() == "org.telegram.messenger" + || category.startsWith("harbour.sailorgram")) { + n.setType(Notification::NotificationTypeTelegram); + n.setSourceName("Telegram"); + } else if (hints.value("x-nemo-origin-package").toString() == "com.google.android.apps.babel" + || owner == "harbour-hangish") { + n.setType(Notification::NotificationTypeHangout); + n.setSourceName("Hangouts"); + } else if (hints.value("x-nemo-origin-package").toString() == "com.whatsapp" + || owner.toLower().contains("whatsup")) { + n.setType(Notification::NotificationTypeWhatsApp); + n.setSourceName("Whatsapp"); + } else if (app_name.contains("indicator-datetime")) { + n.setType(Notification::NotificationTypeReminder); + n.setSourceName("reminders"); + } else { + n.setType(Notification::NotificationTypeGeneric); + } + n.setSender(summary); + n.setBody(body); + foreach (const QString &action, actions) { + if (action == "default") { + n.setActToken(hints.value("x-nemo-remote-action-default").toString()); + break; + } + } + qDebug() << "have act token" << n.actToken(); + + emit notificationReceived(n); + // We never return something. We're just snooping in... + setDelayedReply(true); + return 0; +} + + QHash<QString, QString> SailfishPlatform::getCategoryParams(QString category) + { + if (!category.isEmpty()) { + QString categoryConfigFile = QString("/usr/share/lipstick/notificationcategories/%1.conf").arg(category); + QFile testFile(categoryConfigFile); + if (testFile.exists()) { + QHash<QString, QString> categories; + QSettings settings(categoryConfigFile, QSettings::IniFormat); + const QStringList settingKeys = settings.allKeys(); + foreach (const QString &settingKey, settingKeys) { + categories[settingKey] = settings.value(settingKey).toString(); + } + return categories; + } + } + return QHash<QString, QString>(); + } + +void SailfishPlatform::sendMusicControlCommand(MusicControlButton controlButton) +{ + QString method; + switch (controlButton) { + case MusicControlPlayPause: + method = "PlayPause"; + break; + case MusicControlSkipBack: + method = "Previous"; + break; + case MusicControlSkipNext: + method = "Next"; + break; + default: + ; + } + + if (!method.isEmpty()) { + QDBusMessage call = QDBusMessage::createMethodCall(m_mprisService, "/org/mpris/MediaPlayer2", "org.mpris.MediaPlayer2.Player", method); + QDBusError err = QDBusConnection::sessionBus().call(call); + + if (err.isValid()) { + qWarning() << "Error calling mpris method on" << m_mprisService << ":" << err.message(); + } + return; + } + + if (controlButton == MusicControlVolumeUp || controlButton == MusicControlVolumeDown) { + QDBusMessage call = QDBusMessage::createMethodCall("com.Meego.MainVolume2", "/com/meego/mainvolume2", + "org.freedesktop.DBus.Properties", "Get"); + call << "com.Meego.MainVolume2" << "CurrentStep"; + + QDBusReply<QDBusVariant> volumeReply = _pulseBus->call(call); + if (volumeReply.isValid()) { + // Decide the new value for volume, taking limits into account + uint volume = volumeReply.value().variant().toUInt(); + uint newVolume; + qDebug() << "Current volume: " << volumeReply.value().variant().toUInt(); + if (controlButton == MusicControlVolumeUp && volume < _maxVolume-1 ) { + newVolume = volume + 1; + } + else if (controlButton == MusicControlVolumeDown && volume > 0) { + newVolume = volume - 1; + } + else { + qDebug() << "Volume already at limit"; + newVolume = volume; + } + + // If we have a new volume level, change it + if (newVolume != volume) { + qDebug() << "Setting volume: " << newVolume; + + call = QDBusMessage::createMethodCall("com.Meego.MainVolume2", "/com/meego/mainvolume2", + "org.freedesktop.DBus.Properties", "Set"); + call << "com.Meego.MainVolume2" << "CurrentStep" << QVariant::fromValue(QDBusVariant(newVolume)); + + QDBusError err = _pulseBus->call(call); + if (err.isValid()) { + qWarning() << err.message(); + } + } + } + } +} + +MusicMetaData SailfishPlatform::musicMetaData() const +{ + return m_musicMetaData; +} + +void SailfishPlatform::hangupCall(uint cookie) +{ + m_telepathyMonitor->hangupCall(cookie); +} + +QList<CalendarEvent> SailfishPlatform::organizerItems() const +{ + return m_organizerAdapter->items(); +} + +void SailfishPlatform::actionTriggered(const QString &actToken) +{ + QVariantMap action; + // Extract the element of the DBus call + QStringList elements(actToken.split(' ', QString::SkipEmptyParts)); + if (elements.size() <= 3) { + qWarning() << "Unable to decode invalid remote action:" << actToken; + } else { + int index = 0; + action.insert(QStringLiteral("service"), elements.at(index++)); + action.insert(QStringLiteral("path"), elements.at(index++)); + action.insert(QStringLiteral("iface"), elements.at(index++)); + action.insert(QStringLiteral("method"), elements.at(index++)); + + if (index < elements.size()) { + QVariantList args; + while (index < elements.size()) { + const QString &arg(elements.at(index++)); + const QByteArray buffer(QByteArray::fromBase64(arg.toUtf8())); + + QDataStream stream(buffer); + QVariant var; + stream >> var; + args.append(var); + } + action.insert(QStringLiteral("arguments"), args); + } + qDebug() << "Calling: " << action; + QDBusMessage call = QDBusMessage::createMethodCall(action.value("service").toString(), action.value("path").toString(), action.value("iface").toString(), action.value("method").toString()); + if (action.contains("arguments")) call.setArguments(action.value("arguments").toList()); + QDBusConnection::sessionBus().call(call); + } +} + +void SailfishPlatform::fetchMusicMetadata() +{ + if (!m_mprisService.isEmpty()) { + QDBusMessage call = QDBusMessage::createMethodCall(m_mprisService, "/org/mpris/MediaPlayer2", "org.freedesktop.DBus.Properties", "Get"); + call << "org.mpris.MediaPlayer2.Player" << "Metadata"; + QDBusPendingCall pcall = QDBusConnection::sessionBus().asyncCall(call); + QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pcall, this); + connect(watcher, &QDBusPendingCallWatcher::finished, this, &SailfishPlatform::fetchMusicMetadataFinished); + } +} + +void SailfishPlatform::fetchMusicMetadataFinished(QDBusPendingCallWatcher *watcher) +{ + watcher->deleteLater(); + QDBusReply<QDBusVariant> reply = watcher->reply(); + if (reply.isValid()) { + QVariantMap curMetadata = qdbus_cast<QVariantMap>(reply.value().variant().value<QDBusArgument>()); + m_musicMetaData.artist = curMetadata.value("xesam:artist").toString(); + m_musicMetaData.album = curMetadata.value("xesam:album").toString(); + m_musicMetaData.title = curMetadata.value("xesam:title").toString(); + emit musicMetadataChanged(m_musicMetaData); + } else { + qWarning() << reply.error().message(); + } +} + +void SailfishPlatform::mediaPropertiesChanged(const QString &interface, const QVariantMap &changedProps, const QStringList &invalidatedProps) +{ + Q_UNUSED(interface) + Q_UNUSED(changedProps) + Q_UNUSED(invalidatedProps) + fetchMusicMetadata(); +} |
