Merge lp:~xavi-garcia-mena/indicator-sound/next-play-prev-buttons-bug-1373313 into lp:indicator-sound/15.04

Proposed by Xavi Garcia
Status: Merged
Approved by: Xavi Garcia
Approved revision: 507
Merged at revision: 497
Proposed branch: lp:~xavi-garcia-mena/indicator-sound/next-play-prev-buttons-bug-1373313
Merge into: lp:indicator-sound/15.04
Prerequisite: lp:~xavi-garcia-mena/indicator-sound/sync-vivid-wily
Diff against target: 488 lines (+269/-85)
7 files modified
debian/changelog (+18/-0)
src/media-player-mpris.vala (+22/-0)
src/media-player.vala (+4/-0)
src/mpris2-interfaces.vala (+4/-1)
src/sound-menu.vala (+61/-6)
tests/media-player-mock.vala (+6/-0)
tests/sound-menu.cc (+154/-78)
To merge this branch: bzr merge lp:~xavi-garcia-mena/indicator-sound/next-play-prev-buttons-bug-1373313
Reviewer Review Type Date Requested Status
Xavi Garcia Approve
PS Jenkins bot (community) continuous-integration Approve
Review via email: mp+273234@code.launchpad.net

Commit message

Synchronize the state of the Previous/Play/Next buttons with the state obtained from MPRIS.

The state is updated every time the properties are modified.

Description of the change

Synchronize the state of the Previous/Play/Next buttons with the state obtained from MPRIS.

The state is updated every time the properties are modified.

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Xavi Garcia (xavi-garcia-mena) wrote :

Approving as this code change was already reviewed and approved by Charles.
This MP is only for vivid.

review: Approve
508. By Xavi Garcia

Fixed version in changelog

509. By Xavi Garcia

Updating MPRIS control in the menu not only when next and previous are updated

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'debian/changelog'
2--- debian/changelog 2015-10-14 14:58:09 +0000
3+++ debian/changelog 2015-10-14 14:58:09 +0000
4@@ -1,3 +1,21 @@
5+indicator-sound (12.10.2+15.04.20150917-0ubuntu1) vivid; urgency=medium
6+
7+ [ Xavi Garcia Mena ]
8+ * Merged lp:~xavi-garcia-mena/indicator-sound/icon-volume-zero
9+
10+ -- Sebastien Bacher <seb128@ubuntu.com> Tue, 15 Sep 2015 08:19:51 +0000
11+
12+indicator-sound (12.10.2+15.04.20150915-0ubuntu1) vivid; urgency=medium
13+
14+ [ Sebastien Bacher ]
15+ * under unity8 start system-settings instead unity-control-center (LP:
16+ #1489427)
17+
18+ [ Xavi Garcia Mena ]
19+ * Merged lp:~xavi-garcia-mena/indicator-sound/icon-volume-zero
20+
21+ -- Sebastien Bacher <seb128@ubuntu.com> Tue, 15 Sep 2015 08:19:51 +0000
22+
23 indicator-sound (12.10.2+15.04.20150812.3-0ubuntu1) vivid; urgency=medium
24
25 [ CI Train Bot ]
26
27=== modified file 'src/media-player-mpris.vala'
28--- src/media-player-mpris.vala 2015-03-26 15:56:49 +0000
29+++ src/media-player-mpris.vala 2015-10-14 14:58:09 +0000
30@@ -79,6 +79,24 @@
31 }
32 }
33
34+ public override bool can_do_play {
35+ get {
36+ return this.proxy.CanPlay;
37+ }
38+ }
39+
40+ public override bool can_do_prev {
41+ get {
42+ return this.proxy.CanGoPrevious;
43+ }
44+ }
45+
46+ public override bool can_do_next {
47+ get {
48+ return this.proxy.CanGoNext;
49+ }
50+ }
51+
52 /**
53 * Attach this object to a process of the associated media player. The player must own @dbus_name and
54 * implement the org.mpris.MediaPlayer2.Player interface.
55@@ -272,6 +290,10 @@
56 if (changed_properties.lookup ("PlaybackStatus", "s", null)) {
57 this.state = this.proxy.PlaybackStatus != null ? this.proxy.PlaybackStatus : "Unknown";
58 }
59+ if (changed_properties.lookup ("CanGoNext", "b", null) || changed_properties.lookup ("CanGoPrevious", "b", null) ||
60+ changed_properties.lookup ("CanPlay", "b", null) || changed_properties.lookup ("CanPause", "b", null)) {
61+ this.playbackstatus_changed ();
62+ }
63
64 var metadata = changed_properties.lookup_value ("Metadata", new VariantType ("a{sv}"));
65 if (metadata != null)
66
67=== modified file 'src/media-player.vala'
68--- src/media-player.vala 2014-02-12 16:21:32 +0000
69+++ src/media-player.vala 2015-10-14 14:58:09 +0000
70@@ -26,6 +26,9 @@
71
72 public virtual bool is_running { get { not_implemented(); return false; } }
73 public virtual bool can_raise { get { not_implemented(); return false; } }
74+ public virtual bool can_do_next { get { not_implemented(); return false; } }
75+ public virtual bool can_do_prev { get { not_implemented(); return false; } }
76+ public virtual bool can_do_play { get { not_implemented(); return false; } }
77
78 public class Track : Object {
79 public string artist { get; construct; }
80@@ -44,6 +47,7 @@
81 }
82
83 public signal void playlists_changed ();
84+ public signal void playbackstatus_changed ();
85
86 public abstract void activate ();
87 public abstract void play_pause ();
88
89=== modified file 'src/mpris2-interfaces.vala'
90--- src/mpris2-interfaces.vala 2015-03-26 15:56:49 +0000
91+++ src/mpris2-interfaces.vala 2015-10-14 14:58:09 +0000
92@@ -37,7 +37,10 @@
93 // properties
94 public abstract HashTable<string, Variant?> Metadata{owned get; set;}
95 public abstract int32 Position{owned get; set;}
96- public abstract string? PlaybackStatus{owned get; set;}
97+ public abstract string? PlaybackStatus{owned get; set;}
98+ public abstract bool CanPlay{owned get; set;}
99+ public abstract bool CanGoNext{owned get; set;}
100+ public abstract bool CanGoPrevious{owned get; set;}
101 // methods
102 public abstract async void PlayPause() throws IOError;
103 public abstract async void Next() throws IOError;
104
105=== modified file 'src/sound-menu.vala'
106--- src/sound-menu.vala 2015-02-12 00:44:34 +0000
107+++ src/sound-menu.vala 2015-10-14 14:58:09 +0000
108@@ -157,18 +157,26 @@
109 this.update_playlists (player);
110
111 var handler_id = player.notify["is-running"].connect ( () => {
112- if (player.is_running)
113- if (this.find_player_section(player) == -1)
114+ if (player.is_running) {
115+ int index = this.find_player_section(player);
116+ if (index == -1) {
117 this.insert_player_section (player);
118- else
119+ }
120+ else {
121+ update_player_section (player, index);
122+ }
123+ }
124+ else {
125 if (this.hide_inactive)
126 this.remove_player_section (player);
127+ }
128
129 this.update_playlists (player);
130 });
131 this.notify_handlers.insert (player, handler_id);
132
133 player.playlists_changed.connect (this.update_playlists);
134+ player.playbackstatus_changed.connect (this.update_playbackstatus);
135 }
136
137 public void remove_player (MediaPlayer player) {
138@@ -215,6 +223,23 @@
139 return -1;
140 }
141
142+ MenuItem create_playback_menu_item (MediaPlayer player) {
143+ var playback_item = new MenuItem (null, null);
144+ playback_item.set_attribute ("x-canonical-type", "s", "com.canonical.unity.playback-item");
145+ if (player.is_running) {
146+ if (player.can_do_play) {
147+ playback_item.set_attribute ("x-canonical-play-action", "s", "indicator.play." + player.id);
148+ }
149+ if (player.can_do_next) {
150+ playback_item.set_attribute ("x-canonical-next-action", "s", "indicator.next." + player.id);
151+ }
152+ if (player.can_do_prev) {
153+ playback_item.set_attribute ("x-canonical-previous-action", "s", "indicator.previous." + player.id);
154+ }
155+ }
156+ return playback_item;
157+ }
158+
159 void insert_player_section (MediaPlayer player) {
160 if (this.hide_players)
161 return;
162@@ -240,9 +265,21 @@
163
164 var playback_item = new MenuItem (null, null);
165 playback_item.set_attribute ("x-canonical-type", "s", "com.canonical.unity.playback-item");
166- playback_item.set_attribute ("x-canonical-play-action", "s", "indicator.play." + player.id);
167- playback_item.set_attribute ("x-canonical-next-action", "s", "indicator.next." + player.id);
168- playback_item.set_attribute ("x-canonical-previous-action", "s", "indicator.previous." + player.id);
169+ playback_item.set_attribute ("x-canonical-play-action", "s", "indicator.play." + player.id + ".disabled");
170+ playback_item.set_attribute ("x-canonical-next-action", "s", "indicator.next." + player.id + ".disabled");
171+ playback_item.set_attribute ("x-canonical-previous-action", "s", "indicator.previous." + player.id + ".disabled");
172+
173+ if (player.is_running) {
174+ if (player.can_do_play) {
175+ playback_item.set_attribute ("x-canonical-play-action", "s", "indicator.play." + player.id);
176+ }
177+ if (player.can_do_next) {
178+ playback_item.set_attribute ("x-canonical-next-action", "s", "indicator.next." + player.id);
179+ }
180+ if (player.can_do_prev) {
181+ playback_item.set_attribute ("x-canonical-previous-action", "s", "indicator.previous." + player.id);
182+ }
183+ }
184 section.append_item (playback_item);
185
186 /* Add new players to the end of the player sections, just before the settings */
187@@ -262,6 +299,17 @@
188 this.menu.remove (index);
189 }
190
191+ void update_player_section (MediaPlayer player, int index) {
192+ var player_section = this.menu.get_item_link(index, Menu.LINK_SECTION) as Menu;
193+ if (player_section.get_n_items () == 2) {
194+ // we have 2 items, the second one is the playback item
195+ // remove it first
196+ player_section.remove (1);
197+ MenuItem playback_item = create_playback_menu_item (player);
198+ player_section.append_item (playback_item);
199+ }
200+ }
201+
202 void update_playlists (MediaPlayer player) {
203 int index = find_player_section (player);
204 if (index < 0)
205@@ -292,6 +340,13 @@
206 submenu.append_section (null, playlists_section);
207 player_section.append_submenu (_("Choose Playlist"), submenu);
208 }
209+
210+ void update_playbackstatus (MediaPlayer player) {
211+ int index = find_player_section (player);
212+ if (index != -1) {
213+ update_player_section (player, index);
214+ }
215+ }
216
217 MenuItem create_slider_menu_item (string label, string action, double min, double max, double step, string min_icon_name, string max_icon_name) {
218 var min_icon = new ThemedIcon.with_default_fallbacks (min_icon_name);
219
220=== modified file 'tests/media-player-mock.vala'
221--- tests/media-player-mock.vala 2014-02-12 19:54:59 +0000
222+++ tests/media-player-mock.vala 2015-10-14 14:58:09 +0000
223@@ -28,6 +28,9 @@
224
225 public override bool is_running { get { return mock_is_running; } }
226 public override bool can_raise { get { return mock_can_raise; } }
227+ public override bool can_do_next { get { return mock_can_do_next; } }
228+ public override bool can_do_prev { get { return mock_can_do_prev; } }
229+ public override bool can_do_play { get { return mock_can_do_play; } }
230
231 public override MediaPlayer.Track? current_track { get { return mock_current_track; } set { this.mock_current_track = value; } }
232
233@@ -40,6 +43,9 @@
234
235 public bool mock_is_running { get; set; }
236 public bool mock_can_raise { get; set; }
237+ public bool mock_can_do_next { get; set; }
238+ public bool mock_can_do_prev { get; set; }
239+ public bool mock_can_do_play { get; set; }
240
241 public MediaPlayer.Track? mock_current_track { get; set; }
242
243
244=== modified file 'tests/sound-menu.cc'
245--- tests/sound-menu.cc 2014-03-03 21:38:38 +0000
246+++ tests/sound-menu.cc 2015-10-14 14:58:09 +0000
247@@ -27,87 +27,163 @@
248
249 class SoundMenuTest : public ::testing::Test
250 {
251- protected:
252- GTestDBus * bus = nullptr;
253-
254- virtual void SetUp() {
255- bus = g_test_dbus_new(G_TEST_DBUS_NONE);
256- g_test_dbus_up(bus);
257- }
258-
259- virtual void TearDown() {
260- g_test_dbus_down(bus);
261- g_clear_object(&bus);
262- }
263-
264- void verify_item_attribute (GMenuModel * mm, guint index, const gchar * name, GVariant * value) {
265- g_variant_ref_sink(value);
266-
267- gchar * variantstr = g_variant_print(value, TRUE);
268- g_debug("Expecting item %d to have a '%s' attribute: %s", index, name, variantstr);
269-
270- const GVariantType * type = g_variant_get_type(value);
271- GVariant * itemval = g_menu_model_get_item_attribute_value(mm, index, name, type);
272-
273- ASSERT_NE(nullptr, itemval);
274- EXPECT_TRUE(g_variant_equal(itemval, value));
275-
276- g_variant_unref(value);
277- }
278+ protected:
279+ GTestDBus * bus = nullptr;
280+
281+ virtual void SetUp() {
282+ bus = g_test_dbus_new(G_TEST_DBUS_NONE);
283+ g_test_dbus_up(bus);
284+ }
285+
286+ virtual void TearDown() {
287+ g_test_dbus_down(bus);
288+ g_clear_object(&bus);
289+ }
290+
291+ void verify_item_attribute (GMenuModel * mm, guint index, const gchar * name, GVariant * value) {
292+ g_variant_ref_sink(value);
293+
294+ gchar * variantstr = g_variant_print(value, TRUE);
295+ g_debug("Expecting item %d to have a '%s' attribute: %s", index, name, variantstr);
296+
297+ const GVariantType * type = g_variant_get_type(value);
298+ GVariant * itemval = g_menu_model_get_item_attribute_value(mm, index, name, type);
299+
300+ ASSERT_NE(nullptr, itemval);
301+ EXPECT_TRUE(g_variant_equal(itemval, value));
302+
303+ g_variant_unref(value);
304+ }
305+
306+ void verify_item_attribute_is_not_set(GMenuModel * mm, guint index, const gchar * name, const GVariantType * type) {
307+ GVariant * itemval = g_menu_model_get_item_attribute_value(mm, index, name, type);
308+ EXPECT_EQ(itemval, nullptr);
309+ }
310+
311+ void check_player_control_buttons(bool canPlay, bool canNext, bool canPrev)
312+ {
313+ SoundMenu * menu = sound_menu_new (nullptr, SOUND_MENU_DISPLAY_FLAGS_NONE);
314+
315+ MediaPlayerTrack * track = media_player_track_new("Artist", "Title", "Album", "http://art.url");
316+
317+ MediaPlayerMock * media = MEDIA_PLAYER_MOCK(
318+ g_object_new(TYPE_MEDIA_PLAYER_MOCK,
319+ "mock-id", "player-id",
320+ "mock-name", "Test Player",
321+ "mock-state", "Playing",
322+ "mock-is-running", TRUE,
323+ "mock-can-raise", FALSE,
324+ "mock-current-track", track,
325+ "mock-can-do-play", canPlay,
326+ "mock-can-do-next", canNext,
327+ "mock-can-do-prev", canPrev,
328+ NULL)
329+ );
330+ g_clear_object(&track);
331+
332+ sound_menu_add_player(menu, MEDIA_PLAYER(media));
333+
334+ ASSERT_NE(nullptr, menu->menu);
335+ EXPECT_EQ(2, g_menu_model_get_n_items(G_MENU_MODEL(menu->menu)));
336+
337+ GMenuModel * section = g_menu_model_get_item_link(G_MENU_MODEL(menu->menu), 1, G_MENU_LINK_SECTION);
338+ ASSERT_NE(nullptr, section);
339+ EXPECT_EQ(2, g_menu_model_get_n_items(section)); /* No playlists, so two items */
340+
341+ /* Player display */
342+ verify_item_attribute(section, 0, "action", g_variant_new_string("indicator.player-id"));
343+ verify_item_attribute(section, 0, "x-canonical-type", g_variant_new_string("com.canonical.unity.media-player"));
344+
345+ /* Player control */
346+ verify_item_attribute(section, 1, "x-canonical-type", g_variant_new_string("com.canonical.unity.playback-item"));
347+ verify_item_attribute(section, 1, "x-canonical-play-action", g_variant_new_string(canPlay ? "indicator.play.player-id" : "indicator.play.player-id.disabled"));
348+ verify_item_attribute(section, 1, "x-canonical-next-action", g_variant_new_string(canNext ? "indicator.next.player-id" : "indicator.next.player-id.disabled"));
349+ verify_item_attribute(section, 1, "x-canonical-previous-action", g_variant_new_string(canPrev ? "indicator.previous.player-id" : "indicator.previous.player-id.disabled"));
350+
351+ g_clear_object(&section);
352+
353+ sound_menu_remove_player(menu, MEDIA_PLAYER(media));
354+
355+ EXPECT_EQ(1, g_menu_model_get_n_items(G_MENU_MODEL(menu->menu)));
356+
357+ g_clear_object(&media);
358+ g_clear_object(&menu);
359+ }
360 };
361
362 TEST_F(SoundMenuTest, BasicObject) {
363- SoundMenu * menu = sound_menu_new (nullptr, SOUND_MENU_DISPLAY_FLAGS_NONE);
364-
365- ASSERT_NE(nullptr, menu);
366-
367- g_clear_object(&menu);
368- return;
369+ SoundMenu * menu = sound_menu_new (nullptr, SOUND_MENU_DISPLAY_FLAGS_NONE);
370+
371+ ASSERT_NE(nullptr, menu);
372+
373+ g_clear_object(&menu);
374+ return;
375 }
376
377 TEST_F(SoundMenuTest, AddRemovePlayer) {
378- SoundMenu * menu = sound_menu_new (nullptr, SOUND_MENU_DISPLAY_FLAGS_NONE);
379-
380- MediaPlayerTrack * track = media_player_track_new("Artist", "Title", "Album", "http://art.url");
381-
382- MediaPlayerMock * media = MEDIA_PLAYER_MOCK(
383- g_object_new(TYPE_MEDIA_PLAYER_MOCK,
384- "mock-id", "player-id",
385- "mock-name", "Test Player",
386- "mock-state", "Playing",
387- "mock-is-running", TRUE,
388- "mock-can-raise", FALSE,
389- "mock-current-track", track,
390- NULL)
391- );
392- g_clear_object(&track);
393-
394- sound_menu_add_player(menu, MEDIA_PLAYER(media));
395-
396- ASSERT_NE(nullptr, menu->menu);
397- EXPECT_EQ(2, g_menu_model_get_n_items(G_MENU_MODEL(menu->menu)));
398-
399- GMenuModel * section = g_menu_model_get_item_link(G_MENU_MODEL(menu->menu), 1, G_MENU_LINK_SECTION);
400- ASSERT_NE(nullptr, section);
401- EXPECT_EQ(2, g_menu_model_get_n_items(section)); /* No playlists, so two items */
402-
403- /* Player display */
404- verify_item_attribute(section, 0, "action", g_variant_new_string("indicator.player-id"));
405- verify_item_attribute(section, 0, "x-canonical-type", g_variant_new_string("com.canonical.unity.media-player"));
406-
407- /* Player control */
408- verify_item_attribute(section, 1, "x-canonical-type", g_variant_new_string("com.canonical.unity.playback-item"));
409- verify_item_attribute(section, 1, "x-canonical-play-action", g_variant_new_string("indicator.play.player-id"));
410- verify_item_attribute(section, 1, "x-canonical-next-action", g_variant_new_string("indicator.next.player-id"));
411- verify_item_attribute(section, 1, "x-canonical-previous-action", g_variant_new_string("indicator.previous.player-id"));
412-
413- g_clear_object(&section);
414-
415- sound_menu_remove_player(menu, MEDIA_PLAYER(media));
416-
417- EXPECT_EQ(1, g_menu_model_get_n_items(G_MENU_MODEL(menu->menu)));
418-
419- g_clear_object(&media);
420- g_clear_object(&menu);
421- return;
422-}
423+ SoundMenu * menu = sound_menu_new (nullptr, SOUND_MENU_DISPLAY_FLAGS_NONE);
424+
425+ MediaPlayerTrack * track = media_player_track_new("Artist", "Title", "Album", "http://art.url");
426+
427+ MediaPlayerMock * media = MEDIA_PLAYER_MOCK(
428+ g_object_new(TYPE_MEDIA_PLAYER_MOCK,
429+ "mock-id", "player-id",
430+ "mock-name", "Test Player",
431+ "mock-state", "Playing",
432+ "mock-is-running", TRUE,
433+ "mock-can-raise", FALSE,
434+ "mock-current-track", track,
435+ "mock-can-do-play", TRUE,
436+ "mock-can-do-next", TRUE,
437+ "mock-can-do-prev", TRUE,
438+ NULL)
439+ );
440+ g_clear_object(&track);
441+
442+ sound_menu_add_player(menu, MEDIA_PLAYER(media));
443+
444+ ASSERT_NE(nullptr, menu->menu);
445+ EXPECT_EQ(2, g_menu_model_get_n_items(G_MENU_MODEL(menu->menu)));
446+
447+ GMenuModel * section = g_menu_model_get_item_link(G_MENU_MODEL(menu->menu), 1, G_MENU_LINK_SECTION);
448+ ASSERT_NE(nullptr, section);
449+ EXPECT_EQ(2, g_menu_model_get_n_items(section)); /* No playlists, so two items */
450+
451+ /* Player display */
452+ verify_item_attribute(section, 0, "action", g_variant_new_string("indicator.player-id"));
453+ verify_item_attribute(section, 0, "x-canonical-type", g_variant_new_string("com.canonical.unity.media-player"));
454+
455+ /* Player control */
456+ verify_item_attribute(section, 1, "x-canonical-type", g_variant_new_string("com.canonical.unity.playback-item"));
457+ verify_item_attribute(section, 1, "x-canonical-play-action", g_variant_new_string("indicator.play.player-id"));
458+ verify_item_attribute(section, 1, "x-canonical-next-action", g_variant_new_string("indicator.next.player-id"));
459+ verify_item_attribute(section, 1, "x-canonical-previous-action", g_variant_new_string("indicator.previous.player-id"));
460+
461+ g_clear_object(&section);
462+
463+ sound_menu_remove_player(menu, MEDIA_PLAYER(media));
464+
465+ EXPECT_EQ(1, g_menu_model_get_n_items(G_MENU_MODEL(menu->menu)));
466+
467+ g_clear_object(&media);
468+ g_clear_object(&menu);
469+ return;
470+}
471+
472+TEST_F(SoundMenuTest, AddRemovePlayerNoPlayNextPrev) {
473+ check_player_control_buttons(false, false, false);
474+}
475+
476+TEST_F(SoundMenuTest, AddRemovePlayerNoNext) {
477+ check_player_control_buttons(true, false, true);
478+}
479+
480+TEST_F(SoundMenuTest, AddRemovePlayerNoPrev) {
481+ check_player_control_buttons(true, true, false);
482+}
483+
484+TEST_F(SoundMenuTest, AddRemovePlayerNoPlay) {
485+ check_player_control_buttons(false, true, true);
486+}
487+
488+//

Subscribers

People subscribed via source and target branches