From 4e10566c9b0c4fb723ad68e44ae4c231e6959323 Mon Sep 17 00:00:00 2001 From: "Javier S. Pedro" Date: Tue, 4 Sep 2012 03:27:30 +0200 Subject: improve media player's watchlet volume change --- qmafwwatchlet/metawatch-digital.qml | 74 +++++++- qmafwwatchlet/qmafwwatchlet.cpp | 5 +- qmafwwatchlet/qmafwwatchlet.h | 6 +- qmafwwatchlet/qmafwwatchlet.pro | 10 +- qmafwwatchlet/qmafwwatchletplayer.cpp | 14 +- qmafwwatchlet/qmafwwatchletplayer.h | 2 + qmafwwatchlet/qmafwwatchletplugin.cpp | 6 +- qmafwwatchlet/qmafwwatchletplugin.h | 2 +- qmafwwatchlet/qmafwwatchletvolumecontrol.cc | 251 ++++++++++++++++++++++++++++ qmafwwatchlet/qmafwwatchletvolumecontrol.h | 53 ++++++ qmapwatchlet/qmapwatchletplugin.cpp | 2 +- sowatch.pro | 3 +- 12 files changed, 404 insertions(+), 24 deletions(-) create mode 100644 qmafwwatchlet/qmafwwatchletvolumecontrol.cc create mode 100644 qmafwwatchlet/qmafwwatchletvolumecontrol.h diff --git a/qmafwwatchlet/metawatch-digital.qml b/qmafwwatchlet/metawatch-digital.qml index d9370a6..02a5460 100644 --- a/qmafwwatchlet/metawatch-digital.qml +++ b/qmafwwatchlet/metawatch-digital.qml @@ -38,16 +38,82 @@ Rectangle { } } + Rectangle { + id: volumeBar + width: 18 + + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.right: parent.right + + color: "white" + + visible: false + + Image { + id: volumeIcon + anchors.top: parent.top + anchors.left: parent.left + anchors.leftMargin: 2 + + source: "volume.png" + } + + Rectangle { + id: volumeBarBox + + anchors.top: volumeIcon.bottom + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom + anchors.leftMargin: 2 + + color: "black" + + Rectangle { + id: volumeBarThing + + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom + anchors.margins: 4 + + visible: volumeBar.visible + height: (volumeControl.volume * (parent.height - anchors.margins*2)) / (volumeControl.max - volumeControl.min) + + color: "white" + } + } + + Timer { + id: autoHideTimer + interval: 3000 + repeat: false + onTriggered: { + volumeBar.visible = false; + } + } + + function show() { + volumeBar.visible = true; + autoHideTimer.restart(); + } + } + Connections { target: watch onButtonPressed : { switch(button) { - case 1: - player.volumeUp(); + case 1: { + volumeControl.up(); + volumeBar.show(); break; - case 2: - player.volumeDown(); + } + case 2: { + volumeControl.down(); + volumeBar.show(); break; + } case 4: player.playPause(); break; diff --git a/qmafwwatchlet/qmafwwatchlet.cpp b/qmafwwatchlet/qmafwwatchlet.cpp index fb537a6..84c5ea9 100644 --- a/qmafwwatchlet/qmafwwatchlet.cpp +++ b/qmafwwatchlet/qmafwwatchlet.cpp @@ -1,6 +1,7 @@ #include #include "qmafwwatchletplayer.h" +#include "qmafwwatchletvolumecontrol.h" #include "qmafwwatchlet.h" using namespace sowatch; @@ -8,7 +9,8 @@ using namespace sowatch; QMafwWatchlet::QMafwWatchlet(WatchServer* server) : DeclarativeWatchlet(server, "com.javispedro.sowatch.qmafw"), _registry(MafwRegistry::instance()), - _player(new QMafwWatchletPlayer(this)) + _player(new QMafwWatchletPlayer(this)), + _volumeControl(new QMafwWatchletVolumeControl(this)) { MafwShared* shared = MafwShared::instance(); @@ -21,6 +23,7 @@ QMafwWatchlet::QMafwWatchlet(WatchServer* server) : connect(this, SIGNAL(deactivated()), _player, SLOT(deactivate())); rootContext()->setContextProperty("player", _player); + rootContext()->setContextProperty("volumeControl", _volumeControl); setSource(QUrl(SOWATCH_QML_DIR "/qmafwwatchlet/" + server->watch()->model() + ".qml")); } diff --git a/qmafwwatchlet/qmafwwatchlet.h b/qmafwwatchlet/qmafwwatchlet.h index d3c2bd2..09cdf81 100644 --- a/qmafwwatchlet/qmafwwatchlet.h +++ b/qmafwwatchlet/qmafwwatchlet.h @@ -9,6 +9,7 @@ namespace sowatch { class QMafwWatchletPlayer; +class QMafwWatchletVolumeControl; class QMafwWatchlet : public DeclarativeWatchlet { @@ -17,8 +18,9 @@ public: explicit QMafwWatchlet(WatchServer* server); private: - MafwRegistry* _registry; - QMafwWatchletPlayer* _player; + MafwRegistry *_registry; + QMafwWatchletPlayer *_player; + QMafwWatchletVolumeControl *_volumeControl; private slots: void handleRendererAdded(const QString & uuid); diff --git a/qmafwwatchlet/qmafwwatchlet.pro b/qmafwwatchlet/qmafwwatchlet.pro index 4a59ff5..8dc89d4 100644 --- a/qmafwwatchlet/qmafwwatchlet.pro +++ b/qmafwwatchlet/qmafwwatchlet.pro @@ -4,16 +4,18 @@ TEMPLATE = lib CONFIG += plugin QT += dbus CONFIG += link_pkgconfig -PKGCONFIG += qmafw qmafw-shared +PKGCONFIG += dbus-glib-1 qmafw qmafw-shared SOURCES += qmafwwatchlet.cpp \ qmafwwatchletplugin.cpp \ - qmafwwatchletplayer.cpp + qmafwwatchletplayer.cpp \ + qmafwwatchletvolumecontrol.cc HEADERS += qmafwwatchlet.h \ qmafwwatchletplugin.h \ - qmafwwatchletplayer.h + qmafwwatchletplayer.h \ + qmafwwatchletvolumecontrol.h -qml_files.files = metawatch-digital.qml icon.png +qml_files.files = metawatch-digital.qml icon.png volume.png LIBS += -L$$OUT_PWD/../libsowatch/ -lsowatch INCLUDEPATH += $$PWD/../libsowatch diff --git a/qmafwwatchlet/qmafwwatchletplayer.cpp b/qmafwwatchlet/qmafwwatchletplayer.cpp index 8f51fbf..0e8b436 100644 --- a/qmafwwatchlet/qmafwwatchletplayer.cpp +++ b/qmafwwatchlet/qmafwwatchletplayer.cpp @@ -11,6 +11,8 @@ using namespace sowatch; +const QLatin1String QMafwWatchletPlayer::_volumeProp("volume"); + QMafwWatchletPlayer::QMafwWatchletPlayer(QMafwWatchlet* watchlet) : QObject(watchlet), _active(false), @@ -102,15 +104,19 @@ void QMafwWatchletPlayer::previous() void QMafwWatchletPlayer::volumeUp() { if (!_renderer) return; - QString prop("volume"); - _renderer->mafwProperty(prop, qobject_cast(this), SLOT(doVolumeUp(QString,QVariant))); + QString prop(_volumeProp); + _renderer->mafwProperty(prop, + qobject_cast(this), + SLOT(doVolumeUp(QString,QVariant))); } void QMafwWatchletPlayer::volumeDown() { if (!_renderer) return; - QString prop("volume"); - _renderer->mafwProperty(prop, qobject_cast(this), SLOT(doVolumeDown(QString,QVariant))); + QString prop(_volumeProp); + _renderer->mafwProperty(prop, + qobject_cast(this), + SLOT(doVolumeDown(QString,QVariant))); } void QMafwWatchletPlayer::setRenderer(MafwRenderer * renderer) diff --git a/qmafwwatchlet/qmafwwatchletplayer.h b/qmafwwatchlet/qmafwwatchletplayer.h index 625635c..fe79689 100644 --- a/qmafwwatchlet/qmafwwatchletplayer.h +++ b/qmafwwatchlet/qmafwwatchletplayer.h @@ -60,6 +60,8 @@ private: QString _rendererArt; QUrl _mediaArt; + static const QLatin1String _volumeProp; + void setRenderer(MafwRenderer*); void reconnect(); diff --git a/qmafwwatchlet/qmafwwatchletplugin.cpp b/qmafwwatchlet/qmafwwatchletplugin.cpp index 713c199..f2ec355 100644 --- a/qmafwwatchlet/qmafwwatchletplugin.cpp +++ b/qmafwwatchlet/qmafwwatchletplugin.cpp @@ -8,10 +8,6 @@ QMafwWatchletPlugin::QMafwWatchletPlugin(QObject *parent) : { } -QMafwWatchletPlugin::~QMafwWatchletPlugin() -{ -} - QStringList QMafwWatchletPlugin::watchlets() { QStringList l; @@ -23,7 +19,7 @@ WatchletPluginInterface::WatchletInfo QMafwWatchletPlugin::describeWatchlet(cons { WatchletInfo info; if (id != "com.javispedro.sowatch.qmafw") return info; - info.name = "Music player"; + info.name = tr("Music player"); info.icon = QUrl::fromLocalFile(SOWATCH_QML_DIR "/qmafwwatchlet/icon.png"); return info; } diff --git a/qmafwwatchlet/qmafwwatchletplugin.h b/qmafwwatchlet/qmafwwatchletplugin.h index 11e99af..7087a1a 100644 --- a/qmafwwatchlet/qmafwwatchletplugin.h +++ b/qmafwwatchlet/qmafwwatchletplugin.h @@ -1,6 +1,7 @@ #ifndef QMAFWPLUGIN_H #define QMAFWPLUGIN_H +#include #include namespace sowatch @@ -13,7 +14,6 @@ class QMafwWatchletPlugin : public QObject, public WatchletPluginInterface public: explicit QMafwWatchletPlugin(QObject *parent = 0); - ~QMafwWatchletPlugin(); QStringList watchlets(); WatchletInfo describeWatchlet(const QString &id); diff --git a/qmafwwatchlet/qmafwwatchletvolumecontrol.cc b/qmafwwatchlet/qmafwwatchletvolumecontrol.cc new file mode 100644 index 0000000..f4876a8 --- /dev/null +++ b/qmafwwatchlet/qmafwwatchletvolumecontrol.cc @@ -0,0 +1,251 @@ +#include +#include + +#include "qmafwwatchletvolumecontrol.h" + +#define DEFAULT_ADDRESS "unix:path=/var/run/pulse/dbus-socket" + +#define VOLUME_SV "com.Nokia.MainVolume1" +#define VOLUME_PATH "/com/meego/mainvolume1" +#define VOLUME_IF "com.Nokia.MainVolume1" + +using namespace sowatch; + +QMafwWatchletVolumeControl::QMafwWatchletVolumeControl(QObject *parent) : + QObject(parent), _conn(0), _curStep(0), _maxStep(1) +{ + _openConnection(); +} + +QMafwWatchletVolumeControl::~QMafwWatchletVolumeControl() +{ + if (_conn) { + dbus_connection_remove_filter(_conn, handleDBusSignal, this); + dbus_connection_unref(_conn); + } +} + +int QMafwWatchletVolumeControl::volume() const +{ + return _curStep; +} + +int QMafwWatchletVolumeControl::max() const +{ + return _maxStep - 1; +} + +int QMafwWatchletVolumeControl::min() const +{ + return 0; +} + +void QMafwWatchletVolumeControl::setVolume(int vol) +{ + if (!_conn) return; + DBusMessage *msg; + quint32 value = vol; + const char *volume_if = VOLUME_IF; + const char *prop = "CurrentStep"; + + msg = dbus_message_new_method_call(VOLUME_SV, VOLUME_PATH, + "org.freedesktop.DBus.Properties", "Set"); + Q_ASSERT(msg); + + dbus_message_append_args(msg, + DBUS_TYPE_STRING, &volume_if, + DBUS_TYPE_STRING, &prop, + DBUS_TYPE_INVALID); + + DBusMessageIter iter, iter_variant; + dbus_message_iter_init_append(msg, &iter); + dbus_message_iter_open_container(&iter, + DBUS_TYPE_VARIANT, + DBUS_TYPE_UINT32_AS_STRING, + &iter_variant); + dbus_message_iter_append_basic(&iter_variant, DBUS_TYPE_UINT32, &value); + dbus_message_iter_close_container(&iter, &iter_variant); + + dbus_connection_send(_conn, msg, 0); + + dbus_message_unref(msg); +} + +void QMafwWatchletVolumeControl::up() +{ + if (_curStep < _maxStep - 1) { + setVolume(_curStep + 1); + } +} + +void QMafwWatchletVolumeControl::down() +{ + if (_curStep > 0) { + setVolume(_curStep - 1); + } +} + +void QMafwWatchletVolumeControl::_openConnection() +{ + DBusError err; + + // Allow for an alternative PulseAudio D-Bus server address + char *pa_bus_address = getenv("PULSE_DBUS_SERVER"); + if (!pa_bus_address) + pa_bus_address = (char *) DEFAULT_ADDRESS; + + dbus_error_init(&err); + + _conn = dbus_connection_open(pa_bus_address, &err); + if (!_conn) { + qWarning() << "Failed to open connection to PulseAudio D-Bus server:" + << err.message; + return; + } + + dbus_connection_setup_with_g_main(_conn, NULL); + dbus_connection_add_filter(_conn, handleDBusSignal, this, 0); + + _listenForSignal(); + _fetchValues(); +} + +void QMafwWatchletVolumeControl::_listenForSignal() +{ + DBusMessage *msg; + const char *signal = "com.Nokia.MainVolume1.StepsUpdated"; + char **empty_array = { 0 }; + + msg = dbus_message_new_method_call(NULL, "/org/pulseaudio/core1", NULL, + "ListenForSignal"); + + if (!msg) { + qWarning() << "Cannot create ListenForSignal message"; + return; + } + + dbus_message_append_args(msg, + DBUS_TYPE_STRING, &signal, + DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH, &empty_array, 0, + DBUS_TYPE_INVALID); + dbus_connection_send(_conn, msg, 0); + + dbus_message_unref(msg); +} + +void QMafwWatchletVolumeControl::_fetchValues() +{ + DBusMessage *msg, *reply; + DBusError err; + + dbus_error_init(&err); + + msg = dbus_message_new_method_call(VOLUME_SV, VOLUME_PATH, + "org.freedesktop.DBus.Properties", + "GetAll"); + if (!msg) { + qWarning() << "Cannot create GetAll message"; + return; + } + + + const char *volume_if = VOLUME_IF; + dbus_message_append_args(msg, + DBUS_TYPE_STRING, &volume_if, + DBUS_TYPE_INVALID); + + DBusPendingCall *pending; + if (!dbus_connection_send_with_reply(_conn, msg, &pending, -1)) { + qWarning() << "Cannot send GetAll message"; + return; + } + + dbus_message_unref(msg); + + if (pending) { + if (!dbus_pending_call_set_notify(pending, handleFetchReply, this, 0)) { + qWarning() << "Cannot set GetAll notify"; + return; + } + } +} + +void QMafwWatchletVolumeControl::handleFetchReply(DBusPendingCall *pending, void *user_data) +{ + QMafwWatchletVolumeControl *self = static_cast(user_data); + DBusMessage *reply = dbus_pending_call_steal_reply(pending); + + if (!reply || dbus_message_get_type(reply) != DBUS_MESSAGE_TYPE_METHOD_RETURN) { + qWarning() << "Received something that is not a method return"; + } + + DBusMessageIter iter; + dbus_message_iter_init(reply, &iter); + while (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_INVALID) { + DBusMessageIter iter_dict; + Q_ASSERT(dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_ARRAY); + dbus_message_iter_recurse(&iter, &iter_dict); + + while (dbus_message_iter_get_arg_type(&iter_dict) != DBUS_TYPE_INVALID) { + DBusMessageIter iter_entry; + Q_ASSERT(dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_ARRAY); + dbus_message_iter_recurse(&iter_dict, &iter_entry); + + char *prop_name; + Q_ASSERT(dbus_message_iter_get_arg_type(&iter_entry) == DBUS_TYPE_STRING); + dbus_message_iter_get_basic(&iter_entry, &prop_name); + dbus_message_iter_next(&iter_entry); + + Q_ASSERT(dbus_message_iter_get_arg_type(&iter_entry) == DBUS_TYPE_VARIANT); + DBusMessageIter iter_variant; + dbus_message_iter_recurse(&iter_entry, &iter_variant); + + if (prop_name && + dbus_message_iter_get_arg_type(&iter_variant) == DBUS_TYPE_UINT32) { + quint32 value; + dbus_message_iter_get_basic(&iter_variant, &value); + + if (strcmp(prop_name, "StepCount")) { + self->_maxStep = value; + } else if (strcmp(prop_name, "CurrentStep")) { + self->_curStep = value; + } + qDebug() << prop_name << value; + } + + dbus_message_iter_next(&iter_dict); + } + + dbus_message_iter_next(&iter); + } + + emit self->maxChanged(); + emit self->volumeChanged(); + + dbus_message_unref(reply); + dbus_pending_call_unref(pending); +} + +DBusHandlerResult QMafwWatchletVolumeControl::handleDBusSignal(DBusConnection *connection, DBusMessage *message, void *user_data) +{ + QMafwWatchletVolumeControl *self = static_cast(user_data); + if (dbus_message_is_signal(message, VOLUME_IF, "StepsUpdated")) { + DBusError err; + quint32 curStep, maxStep; + + dbus_error_init(&err); + if (dbus_message_get_args(message, &err, + DBUS_TYPE_UINT32, &maxStep, + DBUS_TYPE_UINT32, &curStep, + DBUS_TYPE_INVALID)) { + if (self->_maxStep != maxStep) { + self->_maxStep = maxStep; + emit self->maxChanged(); + } + if (self->_curStep != curStep) { + self->_curStep = curStep; + emit self->volumeChanged(); + } + } + } +} diff --git a/qmafwwatchlet/qmafwwatchletvolumecontrol.h b/qmafwwatchlet/qmafwwatchletvolumecontrol.h new file mode 100644 index 0000000..1e1261a --- /dev/null +++ b/qmafwwatchlet/qmafwwatchletvolumecontrol.h @@ -0,0 +1,53 @@ +#ifndef QMAFWWATCHLETVOLUMECONTROL_H +#define QMAFWWATCHLETVOLUMECONTROL_H + +#include +#include + +class MainVolumeControlProxy; + +namespace sowatch +{ + +class QMafwWatchletVolumeControl : public QObject +{ + Q_OBJECT + Q_PROPERTY(int volume READ volume WRITE setVolume NOTIFY volumeChanged) + Q_PROPERTY(int min READ min NOTIFY minChanged) + Q_PROPERTY(int max READ max NOTIFY maxChanged) + +public: + explicit QMafwWatchletVolumeControl(QObject *parent = 0); + ~QMafwWatchletVolumeControl(); + + int volume() const; + int min() const; + int max() const; + + void setVolume(int vol); + +signals: + void volumeChanged(); + void minChanged(); + void maxChanged(); + +public slots: + void up(); + void down(); + +private: + void _openConnection(); + void _listenForSignal(); + void _fetchValues(); + static void handleFetchReply(DBusPendingCall *pending, void *user_data); + static DBusHandlerResult handleDBusSignal(DBusConnection *connection, DBusMessage *message, void *user_data); + +private: + DBusConnection *_conn; + uint _curStep; + uint _maxStep; +}; + +} + +#endif // QMAFWWATCHLETVOLUMECONTROL_H diff --git a/qmapwatchlet/qmapwatchletplugin.cpp b/qmapwatchlet/qmapwatchletplugin.cpp index 32762d0..8f48539 100644 --- a/qmapwatchlet/qmapwatchletplugin.cpp +++ b/qmapwatchlet/qmapwatchletplugin.cpp @@ -42,7 +42,7 @@ WatchletPluginInterface::WatchletInfo QMapWatchletPlugin::describeWatchlet(const info.name = tr("Map"); info.icon = QUrl::fromLocalFile(SOWATCH_QML_DIR "/qmapwatchlet/map-icon.png"); } else if (id == CompassWatchlet::myId) { - info.name = tr("Trip computer"); + info.name = tr("Compass"); info.icon = QUrl::fromLocalFile(SOWATCH_QML_DIR "/qmapwatchlet/compass-icon.png"); } return info; diff --git a/sowatch.pro b/sowatch.pro index dc181de..3a0e025 100644 --- a/sowatch.pro +++ b/sowatch.pro @@ -9,8 +9,7 @@ metawatch.depends = libsowatch # Some watchlets SUBDIRS += notificationswatchlet sysinfowatchlet -SUBDIRS += qmsgwatchlet -SUBDIRS += qmapwatchlet +SUBDIRS += qmsgwatchlet qmapwatchlet notificationswatchlet.depends = libsowatch sysinfowatchlet.depends = libsowatch qmsgwatchlet.depends = libsowatch -- cgit v1.2.3