Merge "qml: QML wouldn't load if there isn't DBUS support builtin"
[apps/mediaplayer.git] / app / dbus.cpp
1 /*
2  * Copyright (C) 2017 Konsulko Group
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include "dbus.h"
18
19
20 DbusService::DbusService(QObject *parent) : QObject(parent)
21 {
22 }
23
24 /*
25  * Light Media Scanner
26  */
27
28 bool DbusService::enableLMS()
29 {
30     QDBusConnection session_bus = QDBusConnection::sessionBus();
31     QDBusConnection system_bus = QDBusConnection::systemBus();
32     bool ret;
33
34     if (!session_bus.isConnected())
35         return false;
36
37     if (!system_bus.isConnected())
38         return false;
39
40     ret = session_bus.connect(QString("org.lightmediascanner"), QString("/org/lightmediascanner/Scanner1"), "org.freedesktop.DBus.Properties", "PropertiesChanged", this, SLOT(lmsUpdate(QString,QVariantMap,QStringList)));
41     if (!ret)
42         return false;
43
44     /* Only subscribe to DeviceRemoved events, since we need lms scan to complete on insert */
45     return system_bus.connect(QString("org.freedesktop.UDisks"), QString("/org/freedesktop/UDisks"), "org.freedesktop.UDisks", "DeviceRemoved", this, SLOT(mediaRemoved(QDBusObjectPath)));
46 }
47
48 void DbusService::mediaRemoved(const QDBusObjectPath&)
49 {
50         emit stopPlayback();
51 }
52
53 #if defined(HAVE_LIGHTMEDIASCANNER)
54 void DbusService::lmsUpdate(const QString&, const QVariantMap& map, const QStringList&)
55 {
56     QVariantList mediaFiles;
57     QString music;
58
59     if (!map.contains("IsScanning") && !map.contains("WriteLocked"))
60         return;
61
62     if (map["IsScanning"].toBool() || map["WriteLocked"].toBool())
63         return;
64
65     mediaFiles = LightMediaScanner::processLightMediaScanner();
66
67     if (!mediaFiles.isEmpty())
68         emit processPlaylistUpdate(mediaFiles);
69     else
70         emit processPlaylistHide();
71 }
72 #else
73 void DbusService::lmsUpdate(const QString&, const QVariantMap&, const QStringList&)
74 {
75 }
76 #endif
77
78 /*
79  * Bluetooth
80  */
81
82 void DbusService::setBluezPath(const QString& path)
83 {
84     this->bluezPath = path;
85 }
86
87 QString DbusService::getBluezPath() const
88 {
89     return this->bluezPath;
90 }
91
92 bool DbusService::enableBluetooth()
93 {
94     QDBusConnection system_bus = QDBusConnection::systemBus();
95     QString interface = "org.freedesktop.DBus.ObjectManager";
96     bool ret;
97
98     if (!system_bus.isConnected())
99         return false;
100
101     if (deviceConnected(system_bus))
102         initialBluetoothData(system_bus);
103
104     ret = system_bus.connect(QString("org.bluez"), QString("/"), interface, "InterfacesAdded", this, SLOT(newBluetoothDevice(QDBusObjectPath,QVariantMap)));
105
106     if (!ret)
107         return false;
108
109     ret = system_bus.connect(QString("org.bluez"), QString("/"), interface, "InterfacesRemoved", this, SLOT(removeBluetoothDevice(QDBusObjectPath,QStringList)));
110
111     /*
112      * Unregister InterfacesAdded on error condition
113      */
114     if (!ret)
115         system_bus.disconnect(QString("org.bluez"), QString("/"), interface, "InterfacesAdded", this, SLOT(newBluetoothDevice(QString,QVariantMap)));
116
117     return ret;
118 }
119
120 bool DbusService::checkIfPlayer(const QString& path) const
121 {
122     QRegExp regex("^.*/player\\d$");
123     if (regex.exactMatch(path))
124         return true;
125
126     return false;
127 }
128
129 bool DbusService::deviceConnected(const QDBusConnection& system_bus)
130 {
131     QDBusInterface interface("org.bluez", "/", "org.freedesktop.DBus.ObjectManager", system_bus);
132     QDBusMessage result = interface.call("GetManagedObjects");
133     const QDBusArgument argument = result.arguments().at(0).value<QDBusArgument>();
134     bool ret = false;
135
136     if (argument.currentType() != QDBusArgument::MapType)
137         return false;
138
139     argument.beginMap();
140
141     while (!argument.atEnd()) {
142         QString key;
143
144         argument.beginMapEntry();
145         argument >> key;
146         argument.endMapEntry();
147
148         ret = checkIfPlayer(key);
149
150         if (ret) {
151             newBluetoothDevice(QDBusObjectPath(key), QVariantMap());
152             break;
153         }
154     }
155
156     argument.endMap();
157
158     return ret;
159 }
160
161 void DbusService::initialBluetoothData(const QDBusConnection& system_bus)
162 {
163     QDBusInterface interface("org.bluez", getBluezPath(), "org.freedesktop.DBus.Properties", system_bus);
164
165     QDBusMessage result = interface.call("GetAll", "org.bluez.MediaPlayer1");
166     const QDBusArgument argument = result.arguments().at(0).value<QDBusArgument>();
167     QString status, artist, title;
168     int position = 0, duration = 0;
169
170     if (argument.currentType() != QDBusArgument::MapType)
171         return;
172
173     argument.beginMap();
174
175     while (!argument.atEnd()) {
176         QString key;
177         QVariant value;
178
179         argument.beginMapEntry();
180         argument >> key >> value;
181
182         if (key == "Position") {
183             position = value.toInt();
184         } else if (key == "Status") {
185             status = value.toString();
186         } else if (key == "Track") {
187             const QDBusArgument argument1 = qvariant_cast<QDBusArgument>(value);
188             QString key1;
189             QVariant value1;
190
191             argument1.beginMap();
192
193             while (!argument1.atEnd()) {
194                 argument1.beginMapEntry();
195                 argument1 >> key1 >> value1;
196                 if (key1 == "Artist")
197                     artist = value1.toString();
198                 else if (key1 == "Title")
199                     title = value1.toString();
200                 else if (key1 == "Duration")
201                     duration = value1.toInt();
202                 argument1.endMapEntry();
203             }
204             argument1.endMap();
205         }
206         argument.endMapEntry();
207     }
208     argument.endMap();
209
210     emit processPlaylistHide();
211     emit displayBluetoothMetadata(artist, title, duration);
212     emit updatePlayerStatus(status);
213     emit updatePosition(position);
214 }
215
216 void DbusService::newBluetoothDevice(const QDBusObjectPath& item, const QVariantMap&)
217 {
218     QString path = item.path();
219     if (!checkIfPlayer(path))
220         return;
221
222     if (!getBluezPath().isEmpty()) {
223         qWarning() << "Another Bluetooth Player already connected";
224         return;
225     }
226
227     emit processPlaylistHide();
228
229     QDBusConnection system_bus = QDBusConnection::systemBus();
230     system_bus.connect(QString("org.bluez"), path, "org.freedesktop.DBus.Properties", "PropertiesChanged", this, SLOT(processBluetoothEvent(QString,QVariantMap,QStringList)));
231
232     setBluezPath(path);
233 }
234
235 void DbusService::removeBluetoothDevice(const QDBusObjectPath& item, const QStringList&)
236 {
237     QString path = item.path();
238     if (!checkIfPlayer(path))
239         return;
240
241     if (getBluezPath().isEmpty()) {
242         qWarning() << "No Bluetooth Player connected";
243         return;
244     }
245
246     QDBusConnection system_bus = QDBusConnection::systemBus();
247     system_bus.disconnect(QString("org.bluez"), path, "org.freedesktop.DBus.Properties", "PropertiesChanged", this, SLOT(processBluetoothEvent(QString,QVariantMap,QStringList)));
248
249     setBluezPath(QString());
250     emit processPlaylistShow();
251 }
252
253 void DbusService::processBluetoothEvent(const QString&, const QVariantMap& map, const QStringList&)
254 {
255     if (map.contains("Track")) {
256         QVariantMap track;
257         map["Track"].value<QDBusArgument>() >> track;
258         emit displayBluetoothMetadata(track["Artist"].toString(), track["Title"].toString(), track["Duration"].toInt());
259     }
260
261     if (map.contains("Position"))
262         emit updatePosition(map["Position"].toInt());
263
264     if (map.contains("Status"))
265         emit updatePlayerStatus(map["Status"].toString());
266 }
267
268 void DbusService::processQMLEvent(const QString& state)
269 {
270     QDBusInterface interface("org.bluez", getBluezPath(), "org.bluez.MediaPlayer1", QDBusConnection::systemBus());
271     interface.call(state);
272 }
273
274 long DbusService::getCurrentPosition()
275 {
276     QDBusInterface interface("org.bluez", getBluezPath(), "org.bluez.MediaPlayer1", QDBusConnection::systemBus());
277     return interface.property("Position").toInt();
278 }