summaryrefslogtreecommitdiffstats
path: root/app
diff options
context:
space:
mode:
Diffstat (limited to 'app')
-rw-r--r--app/MediaPlayer.qml105
-rw-r--r--app/api/BluetoothManager.qml183
-rw-r--r--app/app.pri2
-rw-r--r--app/dbus.cpp224
-rw-r--r--app/dbus.h64
-rw-r--r--app/main.cpp14
-rw-r--r--app/mediaplayer.qrc1
7 files changed, 216 insertions, 377 deletions
diff --git a/app/MediaPlayer.qml b/app/MediaPlayer.qml
index fbd889c..d37e097 100644
--- a/app/MediaPlayer.qml
+++ b/app/MediaPlayer.qml
@@ -25,63 +25,14 @@ import 'api' as API
ApplicationWindow {
id: root
- Item {
- id: bluetooth
- property bool connected: false
- property string state
-
- property string artist
- property string title
- property int duration: 0
- property int position: 0
- property int pos_offset: 0
-
- function disableBluetooth() {
- bluetooth.artist = ''
- bluetooth.title = ''
- bluetooth.duration = 0
- bluetooth.position = 0
- bluetooth.pos_offset = 0
- bluetooth.connected = false
- }
- }
-
API.LightMediaScanner {
id: binding
url: bindingAddress
}
- Connections {
- target: dbus
-
- onProcessPlaylistHide: {
- playlistview.visible = false
- player.stop()
- }
-
- onProcessPlaylistShow: {
- playlistview.visible = true
- bluetooth.disableBluetooth()
- }
-
- onDisplayBluetoothMetadata: {
- if (avrcp_artist)
- bluetooth.artist = avrcp_artist
- if (avrcp_title)
- bluetooth.title = avrcp_title
- bluetooth.duration = avrcp_duration
- bluetooth.pos_offset = 0
- }
-
- onUpdatePlayerStatus: {
- bluetooth.connected = true
- bluetooth.state = status
- }
-
- onUpdatePosition: {
- slider.value = current_position
- bluetooth.position = current_position
- }
+ API.BluetoothManager {
+ id: bluetooth
+ url: bindingAddress
}
MediaPlayer {
@@ -90,20 +41,18 @@ ApplicationWindow {
autoLoad: true
playlist: playlist
- property bool is_bluetooth: false
function time2str(value) {
return Qt.formatTime(new Date(value), 'mm:ss')
}
- onPositionChanged: slider.value = player.position
}
Timer {
id: timer
interval: 250
- running: (bluetooth.connected && bluetooth.state == "playing")
+ running: (bluetooth.av_connected && bluetooth.state == "playing")
repeat: true
onTriggered: {
- bluetooth.position = dbus.getCurrentPosition() - bluetooth.pos_offset
+ bluetooth.position = bluetooth.position + 250
slider.value = bluetooth.position
}
}
@@ -169,13 +118,13 @@ ApplicationWindow {
Label {
id: title
Layout.alignment: Layout.Center
- text: bluetooth.title ? bluetooth.title : (player.metaData.title ? player.metaData.title : '')
+ text: bluetooth.av_connected ? bluetooth.title : (player.metaData.title ? player.metaData.title : '')
horizontalAlignment: Label.AlignHCenter
verticalAlignment: Label.AlignVCenter
}
Label {
Layout.alignment: Layout.Center
- text: bluetooth.artist ? bluetooth.artist : (player.metaData.contributingArtist ? player.metaData.contributingArtist : '')
+ text: bluetooth.av_connected ? bluetooth.artist : (player.metaData.contributingArtist ? player.metaData.contributingArtist : '')
horizontalAlignment: Label.AlignHCenter
verticalAlignment: Label.AlignVCenter
font.pixelSize: title.font.pixelSize * 0.6
@@ -185,10 +134,11 @@ ApplicationWindow {
Slider {
id: slider
Layout.fillWidth: true
- to: bluetooth.connected ? bluetooth.duration : player.duration
- enabled: bluetooth.connected == false
+ to: bluetooth.av_connected ? bluetooth.duration : player.duration
+ enabled: bluetooth.av_connected == false
+ value: bluetooth.av_connected ? bluetooth.position : player.position
function getPosition() {
- if (bluetooth.connected && bluetooth.position) {
+ if (bluetooth.av_connected) {
return player.time2str(bluetooth.position)
}
return player.time2str(player.position)
@@ -205,7 +155,7 @@ ApplicationWindow {
anchors.right: parent.right
anchors.bottom: parent.top
font.pixelSize: 32
- text: bluetooth.connected ? player.time2str(bluetooth.duration) : player.time2str(player.duration)
+ text: bluetooth.av_connected ? player.time2str(bluetooth.duration) : player.time2str(player.duration)
}
onPressedChanged: player.seek(value)
}
@@ -222,9 +172,9 @@ ApplicationWindow {
id: previous
offImage: './images/AGL_MediaPlayer_BackArrow.svg'
onClicked: {
- if (bluetooth.connected) {
- bluetooth.pos_offset = dbus.getCurrentPosition()
- dbus.processQMLEvent("Previous")
+ if (bluetooth.av_connected) {
+ bluetooth.sendMediaCommand("Previous")
+ bluetooth.position = 0
} else {
playlist.previous()
}
@@ -234,8 +184,8 @@ ApplicationWindow {
id: play
offImage: './images/AGL_MediaPlayer_Player_Play.svg'
onClicked: {
- if (bluetooth.connected) {
- dbus.processQMLEvent("Play")
+ if (bluetooth.av_connected) {
+ bluetooth.sendMediaCommand("Play")
} else {
player.play()
}
@@ -250,11 +200,11 @@ ApplicationWindow {
}
},
State {
- when: bluetooth.connected && bluetooth.state == "playing"
+ when: bluetooth.av_connected && bluetooth.state == "playing"
PropertyChanges {
target: play
offImage: './images/AGL_MediaPlayer_Player_Pause.svg'
- onClicked: dbus.processQMLEvent("Pause")
+ onClicked: bluetooth.sendMediaCommand("Pause")
}
}
@@ -264,8 +214,8 @@ ApplicationWindow {
id: forward
offImage: './images/AGL_MediaPlayer_ForwardArrow.svg'
onClicked: {
- if (bluetooth.connected) {
- dbus.processQMLEvent("Next")
+ if (bluetooth.av_connected) {
+ bluetooth.sendMediaCommand("Next")
} else {
playlist.next()
}
@@ -275,10 +225,18 @@ ApplicationWindow {
Item { Layout.fillWidth: true }
ToggleButton {
- enabled: false
- checked: bluetooth.connected
+ enabled: bluetooth.connected
+ checked: bluetooth.av_connected
offImage: './images/AGL_MediaPlayer_Bluetooth_Inactive.svg'
onImage: './images/AGL_MediaPlayer_Bluetooth_Active.svg'
+
+ onClicked: {
+ if (bluetooth.av_connected) {
+ bluetooth.disconnect_profiles()
+ } else {
+ bluetooth.connect_profiles()
+ }
+ }
}
}
}
@@ -297,6 +255,7 @@ ApplicationWindow {
ListView {
anchors.fill: parent
id: playlistview
+ visible: bluetooth.av_connected == false
clip: true
header: Label {
x: 50
diff --git a/app/api/BluetoothManager.qml b/app/api/BluetoothManager.qml
new file mode 100644
index 0000000..bb273a5
--- /dev/null
+++ b/app/api/BluetoothManager.qml
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2016 The Qt Company Ltd.
+ * Copyright (C) 2017 Konsulko Group
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import QtQuick 2.6
+import QtWebSockets 1.0
+
+WebSocket {
+ id: root
+ active: true
+ url: bindingAddress
+
+ property string statusString: "waiting..."
+ property string apiString: "Bluetooth-Manager"
+ property var verbs: []
+ property string payloadLength: "9999"
+
+ property string deviceAddress: ""
+ property bool connected: false
+ property bool av_connected: false
+
+ property int position: 0
+ property int duration: 0
+
+ property string artist: ""
+ property string title: ""
+ property string state: "stopped"
+
+ // AVRCP Target UUID
+ property string avrcp_uuid: "0000110e-0000-1000-8000-00805f9b34fb"
+
+ // A2DP Source
+ property string a2dp_uuid: "0000110a-0000-1000-8000-00805f9b34fb"
+
+ readonly property var msgid: {
+ "call": 2,
+ "retok": 3,
+ "reterr": 4,
+ "event": 5
+ }
+
+ onTextMessageReceived: {
+ var json = JSON.parse(message)
+ console.debug("Raw response: " + message)
+ var request = json[2].request
+ var response = json[2].response
+ console.debug("response: " + JSON.stringify(response))
+ switch (json[0]) {
+ case msgid.call:
+ break
+ case msgid.retok:
+ root.statusString = request.status
+ var address = ""
+
+ if (request.info == "BT - Scan Result is Displayed") {
+ for (var i = 0; i < response.length; i++) {
+ var data = response[i]
+ if (data.Connected == "True" && data.UUIDs.indexOf(avrcp_uuid) >= 0) {
+ address = response[i].Address
+ console.debug("Connected Device: " + address)
+
+ root.connected = true
+ player.pause()
+
+ //NOTE: This hack is here for when MediaPlayer is started
+ // with an existing connection.
+ if (data.AVPConnected == "True") {
+ root.av_connected = true
+ }
+ }
+ }
+ root.deviceAddress = address
+ if (!address) {
+ root.connected = false
+ playlistview.visible = true
+ }
+ }
+ break
+ case msgid.reterr:
+ root.statusString = "Bad return value, binding probably not installed"
+ break
+ case msgid.event:
+ var payload = JSON.parse(JSON.stringify(json[2]))
+ var event = payload.event
+
+ if (event == "Bluetooth-Manager/connection") {
+ sendSocketMessage("discovery_result", 'None')
+ } else if (event == "Bluetooth-Manager/device_updated") {
+ var data = payload.data
+ var metadata = data.Metadata
+
+ if (root.deviceAddress != data.Address)
+ break
+
+ if (data.Connected == "False") {
+ console.debug("Device Disconnected")
+ sendSocketMessage("discovery_result", 'None')
+ break
+ }
+ root.av_connected = data.AVPConnected == "True"
+
+ if ('Position' in metadata) {
+ console.debug("Position " + metadata.Position)
+ root.position = metadata.Position
+ }
+
+ if ('Duration' in metadata) {
+ console.debug("Duration " + metadata.Duration)
+ root.duration = metadata.Duration
+ }
+
+ if ('Status' in metadata) {
+ console.debug("Status " + metadata.Status)
+ root.state = metadata.Status
+ }
+
+ if ('Artist' in metadata) {
+ console.debug("Artist " + metadata.Artist)
+ root.artist = metadata.Artist
+ }
+
+ if ('Title' in metadata) {
+ console.debug("Title " + metadata.Title)
+ root.title = metadata.Title
+ }
+ }
+ break
+ }
+ }
+
+ onStatusChanged: {
+ switch (status) {
+ case WebSocket.Open:
+ console.debug("onStatusChanged: Open")
+ sendSocketMessage("eventadd", { "tag" : "device_updated", "name" : "device_updated" })
+ sendSocketMessage("eventsub", { "tag" : "device_updated" })
+ sendSocketMessage("eventadd", { "tag" : "connection", "name" : "connection" })
+ sendSocketMessage("eventsub", { "tag" : "connection" })
+ sendSocketMessage("discovery_result", 'None')
+ break
+ case WebSocket.Error:
+ root.statusString = "WebSocket error: " + root.errorString
+ break
+ }
+ }
+
+ function sendSocketMessage(verb, parameter) {
+ var requestJson = [ msgid.call, payloadLength, apiString + '/'
+ + verb, parameter ]
+ console.debug("sendSocketMessage: " + JSON.stringify(requestJson))
+ verbs.push(verb)
+ root.sendTextMessage(JSON.stringify(requestJson))
+ }
+
+ function sendMediaCommand(state) {
+ var parameters = { "Address": deviceAddress, "value": state }
+ sendSocketMessage("set_avrcp_controls", parameters)
+ }
+
+ function connect_profiles() {
+ sendSocketMessage("connect", { "value": root.deviceAddress, "uuid": a2dp_uuid })
+ sendSocketMessage("connect", { "value": root.deviceAddress, "uuid": avrcp_uuid })
+ root.av_connected = true
+ }
+
+ function disconnect_profiles() {
+ sendSocketMessage("disconnect", { "value": root.deviceAddress, "uuid": a2dp_uuid })
+ sendSocketMessage("disconnect", { "value": root.deviceAddress, "uuid": avrcp_uuid })
+ }
+}
diff --git a/app/app.pri b/app/app.pri
index 56bc949..7ec39b1 100644
--- a/app/app.pri
+++ b/app/app.pri
@@ -14,8 +14,6 @@ packagesExist(sqlite3 lightmediascanner) {
}
packagesExist(dbus-1) {
- HEADERS += dbus.h
- SOURCES += dbus.cpp
DEFINES += HAVE_DBUS
QT += dbus
}
diff --git a/app/dbus.cpp b/app/dbus.cpp
deleted file mode 100644
index a562989..0000000
--- a/app/dbus.cpp
+++ /dev/null
@@ -1,224 +0,0 @@
-/*
- * Copyright (C) 2017 Konsulko Group
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "dbus.h"
-
-
-DbusService::DbusService(QObject *parent) : QObject(parent)
-{
-}
-
-/*
- * Bluetooth
- */
-
-void DbusService::setBluezPath(const QString& path)
-{
- this->bluezPath = path;
-}
-
-QString DbusService::getBluezPath() const
-{
- return this->bluezPath;
-}
-
-bool DbusService::enableBluetooth()
-{
- QDBusConnection system_bus = QDBusConnection::systemBus();
- QString interface = "org.freedesktop.DBus.ObjectManager";
- bool ret;
-
- if (!system_bus.isConnected())
- return false;
-
- if (deviceConnected(system_bus))
- initialBluetoothData(system_bus);
-
- ret = system_bus.connect(QString("org.bluez"), QString("/"), interface, "InterfacesAdded", this, SLOT(newBluetoothDevice(QDBusObjectPath,QVariantMap)));
-
- if (!ret)
- return false;
-
- ret = system_bus.connect(QString("org.bluez"), QString("/"), interface, "InterfacesRemoved", this, SLOT(removeBluetoothDevice(QDBusObjectPath,QStringList)));
-
- /*
- * Unregister InterfacesAdded on error condition
- */
- if (!ret)
- system_bus.disconnect(QString("org.bluez"), QString("/"), interface, "InterfacesAdded", this, SLOT(newBluetoothDevice(QString,QVariantMap)));
-
- return ret;
-}
-
-bool DbusService::checkIfPlayer(const QString& path) const
-{
- QRegExp regex("^.*/player\\d$");
- if (regex.exactMatch(path))
- return true;
-
- return false;
-}
-
-bool DbusService::deviceConnected(const QDBusConnection& system_bus)
-{
- QDBusInterface interface("org.bluez", "/", "org.freedesktop.DBus.ObjectManager", system_bus);
- QDBusMessage result = interface.call("GetManagedObjects");
- const QDBusArgument argument = result.arguments().at(0).value<QDBusArgument>();
- bool ret = false;
-
- if (argument.currentType() != QDBusArgument::MapType)
- return false;
-
- argument.beginMap();
-
- while (!argument.atEnd()) {
- QString key;
-
- argument.beginMapEntry();
- argument >> key;
- argument.endMapEntry();
-
- ret = checkIfPlayer(key);
-
- if (ret) {
- newBluetoothDevice(QDBusObjectPath(key), QVariantMap());
- break;
- }
- }
-
- argument.endMap();
-
- return ret;
-}
-
-void DbusService::initialBluetoothData(const QDBusConnection& system_bus)
-{
- QDBusInterface interface("org.bluez", getBluezPath(), "org.freedesktop.DBus.Properties", system_bus);
-
- QDBusMessage result = interface.call("GetAll", "org.bluez.MediaPlayer1");
- const QDBusArgument argument = result.arguments().at(0).value<QDBusArgument>();
- QString status, artist, title;
- int position = 0, duration = 0;
-
- if (argument.currentType() != QDBusArgument::MapType)
- return;
-
- argument.beginMap();
-
- while (!argument.atEnd()) {
- QString key;
- QVariant value;
-
- argument.beginMapEntry();
- argument >> key >> value;
-
- if (key == "Position") {
- position = value.toInt();
- } else if (key == "Status") {
- status = value.toString();
- } else if (key == "Track") {
- const QDBusArgument argument1 = qvariant_cast<QDBusArgument>(value);
- QString key1;
- QVariant value1;
-
- argument1.beginMap();
-
- while (!argument1.atEnd()) {
- argument1.beginMapEntry();
- argument1 >> key1 >> value1;
- if (key1 == "Artist")
- artist = value1.toString();
- else if (key1 == "Title")
- title = value1.toString();
- else if (key1 == "Duration")
- duration = value1.toInt();
- argument1.endMapEntry();
- }
- argument1.endMap();
- }
- argument.endMapEntry();
- }
- argument.endMap();
-
- emit processPlaylistHide();
- emit displayBluetoothMetadata(artist, title, duration);
- emit updatePlayerStatus(status);
- emit updatePosition(position);
-}
-
-void DbusService::newBluetoothDevice(const QDBusObjectPath& item, const QVariantMap&)
-{
- QString path = item.path();
- if (!checkIfPlayer(path))
- return;
-
- if (!getBluezPath().isEmpty()) {
- qWarning() << "Another Bluetooth Player already connected";
- return;
- }
-
- emit processPlaylistHide();
-
- QDBusConnection system_bus = QDBusConnection::systemBus();
- system_bus.connect(QString("org.bluez"), path, "org.freedesktop.DBus.Properties", "PropertiesChanged", this, SLOT(processBluetoothEvent(QString,QVariantMap,QStringList)));
-
- setBluezPath(path);
-}
-
-void DbusService::removeBluetoothDevice(const QDBusObjectPath& item, const QStringList&)
-{
- QString path = item.path();
- if (!checkIfPlayer(path))
- return;
-
- if (getBluezPath().isEmpty()) {
- qWarning() << "No Bluetooth Player connected";
- return;
- }
-
- QDBusConnection system_bus = QDBusConnection::systemBus();
- system_bus.disconnect(QString("org.bluez"), path, "org.freedesktop.DBus.Properties", "PropertiesChanged", this, SLOT(processBluetoothEvent(QString,QVariantMap,QStringList)));
-
- setBluezPath(QString());
- emit processPlaylistShow();
-}
-
-void DbusService::processBluetoothEvent(const QString&, const QVariantMap& map, const QStringList&)
-{
- if (map.contains("Track")) {
- QVariantMap track;
- map["Track"].value<QDBusArgument>() >> track;
- emit displayBluetoothMetadata(track["Artist"].toString(), track["Title"].toString(), track["Duration"].toInt());
- }
-
- if (map.contains("Position"))
- emit updatePosition(map["Position"].toInt());
-
- if (map.contains("Status"))
- emit updatePlayerStatus(map["Status"].toString());
-}
-
-void DbusService::processQMLEvent(const QString& state)
-{
- QDBusInterface interface("org.bluez", getBluezPath(), "org.bluez.MediaPlayer1", QDBusConnection::systemBus());
- interface.call(state);
-}
-
-long DbusService::getCurrentPosition()
-{
- QDBusInterface interface("org.bluez", getBluezPath(), "org.bluez.MediaPlayer1", QDBusConnection::systemBus());
- return interface.property("Position").toInt();
-}
diff --git a/app/dbus.h b/app/dbus.h
deleted file mode 100644
index bdc2f4f..0000000
--- a/app/dbus.h
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2017 Konsulko Group
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef DBUS_H
-#define DBUS_H
-
-#include <QtCore/QDebug>
-#include <QtCore/QObject>
-#include <QtCore/QString>
-#include <QtCore/QVariant>
-#include <QtCore/QVariantList>
-#include <QtDBus/QDBusPendingCall>
-#include <QtDBus/QDBusPendingReply>
-#include <QtDBus/QDBusInterface>
-#include <QtDBus/QDBusReply>
-#include <QtDBus/QDBusConnection>
-
-class DbusService : public QObject {
- Q_OBJECT
-public:
- explicit DbusService(QObject *parent = 0);
-
- bool enableBluetooth();
- Q_INVOKABLE void processQMLEvent(const QString&);
- Q_INVOKABLE long getCurrentPosition();
-
-private:
- void setBluezPath(const QString& path);
- QString getBluezPath() const;
- bool checkIfPlayer(const QString& path) const;
- bool deviceConnected(const QDBusConnection& system_bus);
- void initialBluetoothData(const QDBusConnection& system_bus);
- QString bluezPath;
-
-signals:
- void processPlaylistUpdate(const QVariantList& mediaFiles);
- void processPlaylistHide();
- void processPlaylistShow();
-
- void displayBluetoothMetadata(const QString& avrcp_artist, const QString& avrcp_title, const int avrcp_duration);
- void stopPlayback();
- void updatePosition(const int current_position);
- void updatePlayerStatus(const QString status);
-
-private slots:
- void newBluetoothDevice(const QDBusObjectPath&, const QVariantMap&);
- void removeBluetoothDevice(const QDBusObjectPath&, const QStringList&);
- void processBluetoothEvent(const QString&, const QVariantMap&, const QStringList&);
-};
-
-#endif
diff --git a/app/main.cpp b/app/main.cpp
index a5bab67..5ad9577 100644
--- a/app/main.cpp
+++ b/app/main.cpp
@@ -29,10 +29,6 @@
#include <libhomescreen.hpp>
#endif
-#ifdef HAVE_DBUS
-#include "dbus.h"
-#endif
-
#include "playlistwithmetadata.h"
#ifndef HAVE_LIGHTMEDIASCANNER
@@ -105,17 +101,7 @@ int main(int argc, char *argv[])
context->setContextProperty(QStringLiteral("bindingAddress"), bindingAddress);
}
-#if defined(HAVE_DBUS)
- DbusService dbus_service;
- context->setContextProperty("dbus", &dbus_service);
-#endif
-
engine.load(QUrl(QStringLiteral("qrc:/MediaPlayer.qml")));
-#if defined(HAVE_DBUS)
- if (!dbus_service.enableBluetooth())
- qWarning() << "Cannot run enableBluetooth";
-#endif
-
return app.exec();
}
diff --git a/app/mediaplayer.qrc b/app/mediaplayer.qrc
index 22b52e1..15e5288 100644
--- a/app/mediaplayer.qrc
+++ b/app/mediaplayer.qrc
@@ -2,5 +2,6 @@
<qresource prefix="/">
<file>MediaPlayer.qml</file>
<file>api/LightMediaScanner.qml</file>
+ <file>api/BluetoothManager.qml</file>
</qresource>
</RCC>