Merge lp:~fabiozaramella/wingpanel-indicator-sound/implement-bluetooth-controls into lp:~wingpanel-devs/wingpanel-indicator-sound/trunk

Proposed by Fabio Zaramella
Status: Merged
Approved by: Danielle Foré
Approved revision: 155
Merged at revision: 155
Proposed branch: lp:~fabiozaramella/wingpanel-indicator-sound/implement-bluetooth-controls
Merge into: lp:~wingpanel-devs/wingpanel-indicator-sound/trunk
Diff against target: 740 lines (+536/-48)
7 files modified
src/CMakeLists.txt (+4/-0)
src/Services/Adapter.vala (+38/-0)
src/Services/Device.vala (+43/-0)
src/Services/Manager.vala (+257/-0)
src/Services/MediaPlayer.vala (+33/-0)
src/Widgets/MprisGui.vala (+125/-48)
src/Widgets/MprisWidget.vala (+36/-0)
To merge this branch: bzr merge lp:~fabiozaramella/wingpanel-indicator-sound/implement-bluetooth-controls
Reviewer Review Type Date Requested Status
WingPanel Devs Pending
Review via email: mp+322258@code.launchpad.net

Commit message

Implements controls for paired bluetooth devices

Description of the change

This branch implements controls for paired bluetooth devices.

To post a comment you must log in.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'src/CMakeLists.txt'
--- src/CMakeLists.txt 2016-12-22 19:28:43 +0000
+++ src/CMakeLists.txt 2017-04-09 16:26:45 +0000
@@ -22,6 +22,10 @@
22 Services/MprisClient.vala22 Services/MprisClient.vala
23 Services/Settings.vala23 Services/Settings.vala
24 Services/Volume-control.vala24 Services/Volume-control.vala
25 Services/Manager.vala
26 Services/Adapter.vala
27 Services/Device.vala
28 Services/MediaPlayer.vala
25 ${CMAKE_CURRENT_BINARY_DIR}/config.vala29 ${CMAKE_CURRENT_BINARY_DIR}/config.vala
26PACKAGES30PACKAGES
27 wingpanel-2.031 wingpanel-2.0
2832
=== added file 'src/Services/Adapter.vala'
--- src/Services/Adapter.vala 1970-01-01 00:00:00 +0000
+++ src/Services/Adapter.vala 2017-04-09 16:26:45 +0000
@@ -0,0 +1,38 @@
1/*
2 * Copyright (c) 2015-2017 elementary LLC. (http://launchpad.net/wingpanel-indicator-sound)
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public
15 * License along with this program; If not, see <http://www.gnu.org/licenses/>.
16 *
17 */
18
19[DBus (name = "org.bluez.Adapter1")]
20public interface Sound.Services.Adapter : Object {
21 public abstract void remove_device (ObjectPath device) throws IOError;
22 public abstract void set_discovery_filter (HashTable<string, Variant> properties) throws IOError;
23 public abstract void start_discovery () throws IOError;
24 public abstract void stop_discovery () throws IOError;
25
26 public abstract string[] UUIDs { public owned get; private set; }
27 public abstract bool discoverable { public get; public set; }
28 public abstract bool discovering { public get; private set; }
29 public abstract bool pairable { public get; public set; }
30 public abstract bool powered { public get; public set; }
31 public abstract string address { public owned get; private set; }
32 public abstract string alias { public owned get; public set; }
33 public abstract string modalias { public owned get; private set; }
34 public abstract string name { public owned get; private set; }
35 public abstract uint @class { public get; private set; }
36 public abstract uint discoverable_timeout { public get; private set; }
37 public abstract uint pairable_timeout { public get; private set; }
38}
039
=== added file 'src/Services/Device.vala'
--- src/Services/Device.vala 1970-01-01 00:00:00 +0000
+++ src/Services/Device.vala 2017-04-09 16:26:45 +0000
@@ -0,0 +1,43 @@
1/*
2 * Copyright (c) 2015-2017 elementary LLC. (http://launchpad.net/wingpanel-indicator-sound)
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public
15 * License along with this program; If not, see <http://www.gnu.org/licenses/>.
16 *
17 */
18
19[DBus (name = "org.bluez.Device1")]
20public interface Sound.Services.Device : Object {
21 public abstract void cancel_pairing () throws IOError;
22 public abstract void connect () throws IOError;
23 public abstract void connect_profile (string UUID) throws IOError;
24 public abstract void disconnect () throws IOError;
25 public abstract void disconnect_profile (string UUID) throws IOError;
26 public abstract void pair () throws IOError;
27
28 public abstract string[] UUIDs { public owned get; private set; }
29 public abstract bool blocked { public owned get; public set; }
30 public abstract bool connected { public owned get; private set; }
31 public abstract bool legacy_pairing { public owned get; private set; }
32 public abstract bool paired { public owned get; private set; }
33 public abstract bool trusted { public owned get; public set; }
34 public abstract int16 RSSI { public owned get; private set; }
35 public abstract ObjectPath adapter { public owned get; private set; }
36 public abstract string address { public owned get; private set; }
37 public abstract string alias { public owned get; public set; }
38 public abstract string icon { public owned get; private set; }
39 public abstract string modalias { public owned get; private set; }
40 public abstract string name { public owned get; private set; }
41 public abstract uint16 appearance { public owned get; private set; }
42 public abstract uint32 @class { public owned get; private set; }
43}
044
=== added file 'src/Services/Manager.vala'
--- src/Services/Manager.vala 1970-01-01 00:00:00 +0000
+++ src/Services/Manager.vala 2017-04-09 16:26:45 +0000
@@ -0,0 +1,257 @@
1/*
2 * Copyright (c) 2015-2017 elementary LLC. (http://launchpad.net/wingpanel-indicator-sound)
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public
15 * License along with this program; If not, see <http://www.gnu.org/licenses/>.
16 *
17 */
18
19[DBus (name = "org.freedesktop.DBus.ObjectManager")]
20public interface Sound.Services.DBusInterface : Object {
21 public signal void interfaces_added (ObjectPath object_path, HashTable<string, HashTable<string, Variant>> param);
22 public signal void interfaces_removed (ObjectPath object_path, string[] string_array);
23
24 public abstract HashTable<ObjectPath, HashTable<string, HashTable<string, Variant>>> get_managed_objects () throws IOError;
25}
26
27public class Sound.Services.ObjectManager : Object {
28 public signal void global_state_changed (bool enabled, bool connected);
29 public signal void adapter_added (Services.Adapter adapter);
30 public signal void adapter_removed (Services.Adapter adapter);
31 public signal void device_added (Services.Device adapter);
32 public signal void device_removed (Services.Device adapter);
33 public signal void media_player_added (Services.MediaPlayer media_player, string name, string icon);
34 public signal void media_player_removed (Services.MediaPlayer media_player);
35 public signal void media_player_status_changed (string status, string title, string album);
36
37 public bool has_object { get; private set; default = false; }
38 public string media_player_status { get; private set; default = "stopped";}
39 public string current_track_title { get; private set; default = "Not playing";}
40 public string current_track_artist { get; private set;}
41
42 private Services.DBusInterface object_interface;
43 private Gee.HashMap<string, Services.Adapter> adapters;
44 private Gee.HashMap<string, Services.Device> devices;
45 private Gee.HashMap<string, Services.MediaPlayer> media_players;
46
47 public ObjectManager () { }
48
49 construct {
50 adapters = new Gee.HashMap<string, Services.Adapter> (null, null);
51 devices = new Gee.HashMap<string, Services.Device> (null, null);
52 media_players = new Gee.HashMap<string, Services.MediaPlayer> (null, null);
53
54 Bus.get_proxy.begin<Services.DBusInterface> (BusType.SYSTEM, "org.bluez", "/", DBusProxyFlags.NONE, null, (obj, res) => {
55 try {
56 object_interface = Bus.get_proxy.end (res);
57 object_interface.get_managed_objects ().foreach (add_path);
58 object_interface.interfaces_added.connect (add_path);
59 object_interface.interfaces_removed.connect (remove_path);
60 } catch (Error e) {
61 critical (e.message);
62 }
63 });
64 }
65
66 [CCode (instance_pos = -1)]
67 private void add_path (ObjectPath path, HashTable<string, HashTable<string, Variant>> param) {
68 if ("org.bluez.Adapter1" in param) {
69 try {
70 Services.Adapter adapter = Bus.get_proxy_sync (BusType.SYSTEM, "org.bluez", path, DBusProxyFlags.GET_INVALIDATED_PROPERTIES);
71 lock (adapters) {
72 adapters.set (path, adapter);
73 }
74 has_object = true;
75
76 adapter_added (adapter);
77 (adapter as DBusProxy).g_properties_changed.connect ((changed, invalid) => {
78 var powered = changed.lookup_value ("Powered", new VariantType ("b"));
79 if (powered != null) {
80 check_global_state ();
81 }
82 });
83 } catch (Error e) {
84 warning ("Connecting to bluetooth adapter failed: %s", e.message);
85 }
86 } else if ("org.bluez.Device1" in param) {
87 try {
88 Services.Device device = Bus.get_proxy_sync (BusType.SYSTEM, "org.bluez", path, DBusProxyFlags.GET_INVALIDATED_PROPERTIES);
89 if (device.paired) {
90 lock (devices) {
91 devices.set (path, device);
92 }
93
94 device_added (device);
95 }
96
97 (device as DBusProxy).g_properties_changed.connect ((changed, invalid) => {
98 var connected = changed.lookup_value ("Connected", new VariantType ("b"));
99 if (connected != null) {
100 check_global_state ();
101 }
102
103 var paired = changed.lookup_value ("Paired", new VariantType ("b"));
104 if (paired != null) {
105 if (device.paired) {
106 lock (devices) {
107 devices.set (path, device);
108 }
109
110 device_added (device);
111 } else {
112 lock (devices) {
113 devices.unset (path);
114 }
115
116 device_removed (device);
117 }
118 }
119 });
120 } catch (Error e) {
121 warning ("Connecting to bluetooth device failed: %s", e.message);
122 }
123 } else if ("org.bluez.MediaPlayer1" in param) {
124 try {
125 Services.MediaPlayer media_player = Bus.get_proxy_sync (BusType.SYSTEM, "org.bluez", path, DBusProxyFlags.GET_INVALIDATED_PROPERTIES);
126 lock (media_players) {
127 media_players.set (path, media_player);
128 }
129 string device_name = path.substring (0, path.last_index_of("/"));
130 Services.Device cur_device = Bus.get_proxy_sync (BusType.SYSTEM, "org.bluez", device_name, DBusProxyFlags.GET_INVALIDATED_PROPERTIES);
131 media_player_status = media_player.track.lookup ("Title").get_string (null);
132 media_player_added (media_player, cur_device.name, cur_device.icon);
133
134 (media_player as DBusProxy).g_properties_changed.connect ((changed, invalid) => {
135 if (changed.print (true).contains ("Track")) {
136 Variant tmp = changed.lookup_value ("Track", VariantType.DICTIONARY);
137 string title = tmp.lookup_value ("Title", VariantType.STRING).get_string (null);
138 string artist = tmp.lookup_value ("Artist", VariantType.STRING).get_string (null);
139 current_track_title = title;
140 current_track_artist = artist;
141 media_player_status_changed ("", title, artist);
142 } else if (changed.lookup("Status", "s")) {
143 string status = changed.lookup_value ("Status", VariantType.STRING).get_string (null);
144 media_player_status = status;
145 media_player_status_changed (status, "", "");
146 }
147 });
148 } catch (Error e) {
149 warning ("Connecting to bluetooth media player failed: %s", e.message);
150 }
151 }
152 }
153
154 [CCode (instance_pos = -1)]
155 public void remove_path (ObjectPath path) {
156 lock (adapters) {
157 var adapter = adapters.get (path);
158 if (adapter != null) {
159 adapters.unset (path);
160 has_object = !adapters.is_empty;
161
162 adapter_removed (adapter);
163 return;
164 }
165 }
166
167 lock (devices) {
168 var device = devices.get (path);
169 if (device != null) {
170 devices.unset (path);
171 device_removed (device);
172 }
173 }
174
175 lock (media_players) {
176 var media_player = media_players.get (path);
177 if (media_player != null) {
178 media_players.unset (path);
179 media_player_removed (media_player);
180 }
181 }
182 }
183
184 public Gee.Collection<Services.Adapter> get_adapters () {
185 lock (adapters) {
186 return adapters.values;
187 }
188 }
189
190 public Gee.Collection<Services.Device> get_devices () {
191 lock (devices) {
192 return devices.values;
193 }
194 }
195
196 public Services.Adapter? get_adapter_from_path (string path) {
197 lock (adapters) {
198 return adapters.get (path);
199 }
200 }
201
202 private void check_global_state () {
203 global_state_changed (get_global_state (), get_connected ());
204 }
205
206 public bool get_connected () {
207 lock (devices) {
208 foreach (var device in devices.values) {
209 if (device.connected) {
210 return true;
211 }
212 }
213 }
214
215 return false;
216 }
217
218 public bool get_global_state () {
219 lock (adapters) {
220 foreach (var adapter in adapters.values) {
221 if (adapter.powered) {
222 return true;
223 }
224 }
225 }
226
227 return false;
228 }
229
230 public void set_global_state (bool state) {
231 new Thread<void*> (null, () => {
232 lock (devices) {
233 foreach (var device in devices.values) {
234 if (device.connected) {
235 try {
236 device.disconnect ();
237 } catch (Error e) {
238 critical (e.message);
239 }
240 }
241 }
242 }
243
244 lock (adapters) {
245 foreach (var adapter in adapters.values) {
246 adapter.powered = state;
247 }
248 }
249
250 return null;
251 });
252 }
253
254 public void set_last_state () {
255 check_global_state ();
256 }
257}
0258
=== added file 'src/Services/MediaPlayer.vala'
--- src/Services/MediaPlayer.vala 1970-01-01 00:00:00 +0000
+++ src/Services/MediaPlayer.vala 2017-04-09 16:26:45 +0000
@@ -0,0 +1,33 @@
1/*
2 * Copyright (c) 2015-2017 elementary LLC. (http://launchpad.net/wingpanel-indicator-sound)
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public
15 * License along with this program; If not, see <http://www.gnu.org/licenses/>.
16 *
17 * Authored by: Fiorotto Giuliano <mr.fiorotto@gmail.com>
18 * Fabio Zaramella <ffabio.96.x@gmail.com>
19 */
20
21[DBus (name = "org.bluez.MediaPlayer1")]
22public interface Sound.Services.MediaPlayer : Object {
23 public abstract void play () throws IOError;
24 public abstract void pause () throws IOError;
25 public abstract void stop () throws IOError;
26 public abstract void next () throws IOError;
27 public abstract void previous () throws IOError;
28 public abstract void fast_forward () throws IOError;
29 public abstract void rewind () throws IOError;
30 public abstract string name { public owned get; }
31 public abstract string status { public owned get; }
32 public abstract HashTable<string,Variant> track { public owned get; }
33}
034
=== modified file 'src/Widgets/MprisGui.vala'
--- src/Widgets/MprisGui.vala 2017-02-03 20:19:16 +0000
+++ src/Widgets/MprisGui.vala 2017-04-09 16:26:45 +0000
@@ -71,6 +71,7 @@
71 }71 }
7272
73 private Services.MprisClient? client_ = null;73 private Services.MprisClient? client_ = null;
74 private Services.MediaPlayer? mp_client = null;
7475
75 public Services.MprisClient? client {76 public Services.MprisClient? client {
76 get {77 get {
@@ -120,6 +121,22 @@
120 }121 }
121122
122 /**123 /**
124 * Create a new ClientWidget for bluetooth controls
125 *
126 * @param client The underlying MediaPlayer instance to use
127 */
128 public ClientWidget.bluetooth (Services.MediaPlayer media_player_client, string name, string icon){
129 mp_client = media_player_client;
130
131 app_icon = new ThemedIcon (icon);
132 background.set_from_gicon (app_icon, Gtk.IconSize.DIALOG);
133 title_label.set_markup ("<b>%s</b>".printf (Markup.escape_text (name)));
134 artist_label.set_text (NOT_PLAYING);
135
136 update_controls ();
137 }
138
139 /**
123 * Create a new ClientWidget for the default player140 * Create a new ClientWidget for the default player
124 *141 *
125 * @param info The AppInfo of the default music player142 * @param info The AppInfo of the default music player
@@ -194,25 +211,33 @@
194 prev_btn = btn;211 prev_btn = btn;
195 btn.clicked.connect (()=> {212 btn.clicked.connect (()=> {
196 Idle.add (()=> {213 Idle.add (()=> {
197 if (client.player.can_go_previous) {214 if (!Thread.supported ()) {
198 if(!Thread.supported ()) {215 warning ("Threading is not supported. DBus timeout could be blocking UI");
199 warning ("Threading is not supported. DBus timeout could be blocking UI");216 try {
200 try {217 if (mp_client == null && client.player.can_go_previous) {
201 client.player.previous();218 client.player.previous ();
202 } catch (Error e) {219 } else if (mp_client != null) {
203 warning ("Going to previous track probably failed (faulty MPRIS interface): %s", e.message);220 mp_client.previous ();
204 }221 }
205 } else {222 } catch (Error e) {
206 new Thread <void*> ("wingpanel_indicator_sound_dbus_backward_thread", () => {223 warning ("Going to previous track probably failed (faulty MPRIS interface): %s", e.message);
207 try {224 }
208 client.player.previous();225 } else {
209 } catch (Error e) {226 new Thread <void*> ("wingpanel_indicator_sound_dbus_backward_thread", () => {
210 warning ("Going to previous track probably failed (faulty MPRIS interface): %s", e.message);227 try {
211 }228 if (mp_client == null) {
212 return null;229 client.player.previous ();
213 });230 } else if(mp_client != null) {
214 }231 mp_client.previous ();
232 }
233 } catch (Error e) {
234 warning ("Going to previous track probably failed (faulty MPRIS interface): %s", e.message);
235 }
236
237 return null;
238 });
215 }239 }
240
216 return false;241 return false;
217 });242 });
218 });243 });
@@ -224,23 +249,43 @@
224 play_btn = btn;249 play_btn = btn;
225 btn.clicked.connect (()=> {250 btn.clicked.connect (()=> {
226 Idle.add (()=> {251 Idle.add (()=> {
227 if(!Thread.supported ()) {252 if (!Thread.supported ()) {
228 warning ("Threading is not supported. DBus timeout could be blocking UI");253 warning ("Threading is not supported. DBus timeout could be blocking UI");
229 try {254 try {
230 client.player.play_pause();255 if (mp_client == null) {
231 } catch (Error e) {256 client.player.play_pause ();
257 } else if (mp_client != null) {
258 if (mp_client.status == "playing") {
259 mp_client.pause ();
260 } else {
261 mp_client.play ();
262 }
263 update_play_status ();
264 }
265 } catch (Error e) {
232 warning ("Playing/Pausing probably failed (faulty MPRIS interface): %s", e.message);266 warning ("Playing/Pausing probably failed (faulty MPRIS interface): %s", e.message);
233 }267 }
234 } else {268 } else {
235 new Thread <void*> ("wingpanel_indicator_sound_dbus_backward_thread", () => {269 new Thread <void*> ("wingpanel_indicator_sound_dbus_backward_thread", () => {
236 try {270 try {
237 client.player.play_pause();271 if (mp_client == null) {
272 client.player.play_pause ();
273 } else if (mp_client != null) {
274 if (mp_client.status == "playing") {
275 mp_client.pause ();
276 } else {
277 mp_client.play ();
278 }
279 update_play_status ();
280 }
238 } catch (Error e) {281 } catch (Error e) {
239 warning ("Playing/Pausing probably failed (faulty MPRIS interface): %s", e.message);282 warning ("Playing/Pausing probably failed (faulty MPRIS interface): %s", e.message);
240 }283 }
284
241 return null;285 return null;
242 });286 });
243 }287 }
288
244 return false;289 return false;
245 });290 });
246 });291 });
@@ -251,25 +296,33 @@
251 next_btn = btn;296 next_btn = btn;
252 btn.clicked.connect (()=> {297 btn.clicked.connect (()=> {
253 Idle.add (()=> {298 Idle.add (()=> {
254 if (client.player.can_go_next) {299 if(!Thread.supported ()) {
255 if(!Thread.supported ()) {300 warning ("Threading is not supported. DBus timeout could be blocking UI");
256 warning ("Threading is not supported. DBus timeout could be blocking UI");301 try {
302 if (mp_client == null && client.player.can_go_next) {
303 client.player.next ();
304 } else if (mp_client != null) {
305 mp_client.next ();
306 }
307 } catch (Error e) {
308 warning ("Going to next track probably failed (faulty MPRIS interface): %s", e.message);
309 }
310 } else {
311 new Thread <void*> ("wingpanel_indicator_sound_dbus_forward_thread", () => {
257 try {312 try {
258 client.player.next();313 if (mp_client == null) {
259 } catch (Error e) {314 client.player.next ();
315 } else if (mp_client != null) {
316 mp_client.next ();
317 }
318 } catch (Error e) {
260 warning ("Going to next track probably failed (faulty MPRIS interface): %s", e.message);319 warning ("Going to next track probably failed (faulty MPRIS interface): %s", e.message);
261 }320 }
262 } else {321
263 new Thread <void*> ("wingpanel_indicator_sound_dbus_forward_thread", () => {322 return null;
264 try {323 });
265 client.player.next();
266 } catch (Error e) {
267 warning ("Going to next track probably failed (faulty MPRIS interface): %s", e.message);
268 }
269 return null;
270 });
271 }
272 }324 }
325
273 return false;326 return false;
274 });327 });
275 });328 });
@@ -295,7 +348,7 @@
295348
296 private void connect_to_client () {349 private void connect_to_client () {
297 client.prop.properties_changed.connect ((i,p,inv)=> {350 client.prop.properties_changed.connect ((i,p,inv)=> {
298 if (i == "org.mpris.MediaPlayer2.Player") {351 if (i == "org.mpris.MediaPlayer2.Player") {
299 /* Handle mediaplayer2 iface */352 /* Handle mediaplayer2 iface */
300 p.foreach ((k,v)=> {353 p.foreach ((k,v)=> {
301 if (k == "Metadata") {354 if (k == "Metadata") {
@@ -323,17 +376,17 @@
323 try {376 try {
324 close ();377 close ();
325 if (client != null && client.player.can_raise) {378 if (client != null && client.player.can_raise) {
326 if(!Thread.supported ()) {379 if (!Thread.supported ()) {
327 warning ("Threading is not supported. DBus timeout could be blocking UI");380 warning ("Threading is not supported. DBus timeout could be blocking UI");
328 try {381 try {
329 client.player.raise();382 client.player.raise ();
330 } catch (Error e) {383 } catch (Error e) {
331 warning ("Raising the player probably failed (faulty MPRIS interface): %s", e.message);384 warning ("Raising the player probably failed (faulty MPRIS interface): %s", e.message);
332 }385 }
333 } else {386 } else {
334 new Thread <void*> ("wingpanel_indicator_sound_dbus_backward_thread", () => {387 new Thread <void*> ("wingpanel_indicator_sound_dbus_backward_thread", () => {
335 try {388 try {
336 client.player.raise();389 client.player.raise ();
337 } catch (Error e) {390 } catch (Error e) {
338 warning ("Raising the player probably failed (faulty MPRIS interface): %s", e.message);391 warning ("Raising the player probably failed (faulty MPRIS interface): %s", e.message);
339 }392 }
@@ -343,7 +396,7 @@
343 } else if (app_info != null) {396 } else if (app_info != null) {
344 app_info.launch (null, null);397 app_info.launch (null, null);
345 }398 }
346 } catch (Error e) {399 } catch (Error e) {
347 warning ("Could not launch player");400 warning ("Could not launch player");
348 }401 }
349402
@@ -376,11 +429,11 @@
376 private void update_play_status () {429 private void update_play_status () {
377 switch (client.player.playback_status) {430 switch (client.player.playback_status) {
378 case "Playing":431 case "Playing":
379 (play_btn.get_image () as Gtk.Image).set_from_icon_name ("media-playback-pause-symbolic", Gtk.IconSize.LARGE_TOOLBAR);432 (play_btn.get_image () as Gtk.Image).set_from_icon_name ("media-playback-pause-symbolic", Gtk.IconSize.LARGE_TOOLBAR);
380 break;433 break;
381 default:434 default:
382 /* Stopped, Paused */435 /* Stopped, Paused */
383 (play_btn.get_image () as Gtk.Image).set_from_icon_name ("media-playback-start-symbolic", Gtk.IconSize.LARGE_TOOLBAR);436 (play_btn.get_image () as Gtk.Image).set_from_icon_name ("media-playback-start-symbolic", Gtk.IconSize.LARGE_TOOLBAR);
384 break;437 break;
385 }438 }
386 }439 }
@@ -389,8 +442,13 @@
389 * Update prev/next sensitivity based on player requirements442 * Update prev/next sensitivity based on player requirements
390 */443 */
391 private void update_controls () {444 private void update_controls () {
392 prev_btn.set_sensitive (client.player.can_go_previous);445 if (mp_client == null) {
393 next_btn.set_sensitive (client.player.can_go_next);446 prev_btn.set_sensitive (client.player.can_go_previous);
447 next_btn.set_sensitive (client.player.can_go_next);
448 } else {
449 prev_btn.set_sensitive (true);
450 next_btn.set_sensitive (true);
451 }
394 }452 }
395453
396 /**454 /**
@@ -498,4 +556,23 @@
498556
499 return Gdk.pixbuf_get_from_surface (surface, 0, 0, mask_size, mask_size);557 return Gdk.pixbuf_get_from_surface (surface, 0, 0, mask_size, mask_size);
500 }558 }
559
560 public void update_play (string playing, string title, string artist) {
561 if (playing != "") {
562 switch (playing) {
563 case "playing":
564 (play_btn.get_image () as Gtk.Image).set_from_icon_name ("media-playback-pause-symbolic", Gtk.IconSize.LARGE_TOOLBAR);
565 break;
566 default:
567 /* Stopped, Paused */
568 (play_btn.get_image () as Gtk.Image).set_from_icon_name ("media-playback-start-symbolic", Gtk.IconSize.LARGE_TOOLBAR);
569 break;
570 }
571 }
572
573 if (title != "" && artist != "") {
574 title_label.set_markup ("<b>%s</b>".printf (Markup.escape_text (title)));
575 artist_label.set_text (artist);
576 }
577 }
501}578}
502579
=== modified file 'src/Widgets/MprisWidget.vala'
--- src/Widgets/MprisWidget.vala 2017-02-03 20:19:16 +0000
+++ src/Widgets/MprisWidget.vala 2017-04-09 16:26:45 +0000
@@ -21,9 +21,12 @@
2121
22 AppInfo? default_music;22 AppInfo? default_music;
23 ClientWidget default_widget;23 ClientWidget default_widget;
24 ClientWidget bluetooth_widget;
24 HashTable<string,ClientWidget> ifaces;25 HashTable<string,ClientWidget> ifaces;
25 public signal void close ();26 public signal void close ();
2627
28 public Sound.Services.ObjectManager object_manager;
29
27 public MprisWidget() {30 public MprisWidget() {
28 Object (orientation: Gtk.Orientation.VERTICAL, spacing: 1);31 Object (orientation: Gtk.Orientation.VERTICAL, spacing: 1);
2932
@@ -46,6 +49,39 @@
46 pack_start(default_widget, false, false, 0);49 pack_start(default_widget, false, false, 0);
47 }50 }
4851
52 object_manager = new Services.ObjectManager ();
53 object_manager.bind_property ("has-object", this, "visible", GLib.BindingFlags.SYNC_CREATE);
54
55 if (object_manager.has_object) {
56 object_manager.set_last_state ();
57 }
58
59 object_manager.media_player_added.connect ((media_player, name, icon) => {
60 try {
61 bluetooth_widget = new ClientWidget.bluetooth (media_player, name, icon);
62 bluetooth_widget.close.connect (() => {
63 close ();
64 });
65
66 bluetooth_widget.show_all ();
67 pack_start (bluetooth_widget, false, false, 0);
68 } catch (Error e) {
69 warning ("Connecting to bluetooth device failed: %s", e.message);
70 }
71 });
72
73 object_manager.media_player_removed.connect ((media_player) => {
74 debug ("Media player %s removed", media_player.name);
75 bluetooth_widget.destroy ();
76 });
77
78 object_manager.media_player_status_changed.connect ((status, title, artist) => {
79 bluetooth_widget.update_play (status, title, artist);
80 if (status == "playing" && default_widget.client.player.playback_status == "Playing") {
81 default_widget.client.player.play_pause ();
82 }
83 });
84
49 show_all();85 show_all();
50 }86 }
5187

Subscribers

People subscribed via source and target branches

to all changes: