Merge lp:~xavi-garcia-mena/indicator-sound/sync-vivid-wily into lp:indicator-sound/15.04
- sync-vivid-wily
- Merge into trunk.15.04
Proposed by
Xavi Garcia
Status: | Merged |
---|---|
Approved by: | Xavi Garcia |
Approved revision: | 501 |
Merged at revision: | 497 |
Proposed branch: | lp:~xavi-garcia-mena/indicator-sound/sync-vivid-wily |
Merge into: | lp:indicator-sound/15.04 |
Diff against target: |
1204 lines (+616/-182) 10 files modified
data/com.canonical.indicator.sound.gschema.xml (+63/-0) debian/changelog (+42/-0) src/service.vala (+180/-132) src/volume-control-pulse.vala (+162/-15) src/volume-control.vala (+13/-1) tests/CMakeLists.txt (+1/-1) tests/media-player-user.cc (+138/-22) tests/notifications-test.cc (+8/-8) tests/volume-control-mock.vala (+4/-2) tests/volume-control-test.cc (+5/-1) |
To merge this branch: | bzr merge lp:~xavi-garcia-mena/indicator-sound/sync-vivid-wily |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Charles Kerr (community) | Approve | ||
Xavi Garcia | Approve | ||
PS Jenkins bot (community) | continuous-integration | Approve | |
Review via email: mp+273230@code.launchpad.net |
Commit message
Sync vivid and wily branches
Description of the change
Sync vivid and wily branches
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 it is just a merge of commits from wily.
Source code should be in sync now with wily.
review:
Approve
- 502. By Xavi Garcia
-
fixed version number in changelog
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'data/com.canonical.indicator.sound.gschema.xml' |
2 | --- data/com.canonical.indicator.sound.gschema.xml 2014-12-03 15:12:01 +0000 |
3 | +++ data/com.canonical.indicator.sound.gschema.xml 2015-10-13 13:18:26 +0000 |
4 | @@ -39,5 +39,68 @@ |
5 | Whether or not to show the sound indicator in the menu bar. |
6 | </description> |
7 | </key> |
8 | + |
9 | + <!-- VOLUME --> |
10 | + |
11 | + <key name="warning-volume-enabled" type="b"> |
12 | + <default>true</default> |
13 | + <summary>Whether or not to show high volume warnings.</summary> |
14 | + <description> |
15 | + If enabled on a device, a confirmation dialog will be presented to the user if |
16 | + (a) something is plugged into the headphone jack (ie, no warnings for speaker volumes) AND |
17 | + (b) media is playing (ie, no warnings for ringtone volumes) AND |
18 | + (c) the user attempts to set the volume higher than warning-volume-decibels AND |
19 | + (d) the user has not indicated approval in the last warning-volume-approval-ttl seconds. |
20 | + </description> |
21 | + </key> |
22 | + <key name="warning-volume-confirmation-ttl" type="i"> |
23 | + <default>72000</default> |
24 | + <summary>How many seconds a user's warning-volume approval should be remembered.</summary> |
25 | + <description> |
26 | + How long to remember a user's approval of the confirmation dialog discussed in the |
27 | + description of 'warning-volume-enabled'. |
28 | + |
29 | + The default value (72,000 seconds) corresponds to the 20 hours suggested by |
30 | + EU standard EN 60950-1/Al2: “The acknowledgement does not need to be repeated |
31 | + more than once every 20 h of cumulative listening time.” |
32 | + </description> |
33 | + </key> |
34 | + <key name="warning-volume-decibels" type="d"> |
35 | + <default>-8.0</default> |
36 | + <summary>Volume level that triggers a high volume warning.</summary> |
37 | + <description> |
38 | + Volume level that triggers a high volume warning. |
39 | + See warning-volume-enabled for details. |
40 | + </description> |
41 | + </key> |
42 | + |
43 | + <key name="normal-volume-decibels" type="d"> |
44 | + <default>0</default> |
45 | + <summary>Normal output volume.</summary> |
46 | + <description> |
47 | + The maximum volume when com.ubuntu.sound's 'allow-amplified-volume' setting is disabled. |
48 | + |
49 | + To conform with EU standard EN 60950-1/Al2, this value should never be |
50 | + set to a dB that causes headphone sound output to exceed 100 dBA, nor |
51 | + electric output to exceed 150 mV. |
52 | + |
53 | + The default value (0) corresponds to PulseAudio's PA_VOLUME_NORM. |
54 | + </description> |
55 | + </key> |
56 | + |
57 | + <key name="amplified-volume-decibels" type="d"> |
58 | + <default>11</default> |
59 | + <summary>Output volume to use when com.ubuntu.sound's 'allow-amplified-volume' setting is enabled.</summary> |
60 | + <description> |
61 | + The maximum volume when com.ubuntu.sound's 'allow-amplified-volume' setting is enabled. |
62 | + |
63 | + To conform with EU standard EN 60950-1/Al2, this value should never be |
64 | + set to a dB that causes headphone sound output to exceed 100 dBA, nor |
65 | + electric output to exceed 150 mV. |
66 | + |
67 | + The default value (11) corresponds to PulseAudio's PA_VOLUME_UI_MAX. |
68 | + </description> |
69 | + </key> |
70 | + |
71 | </schema> |
72 | </schemalist> |
73 | |
74 | === modified file 'debian/changelog' |
75 | --- debian/changelog 2015-05-08 12:20:07 +0000 |
76 | +++ debian/changelog 2015-10-13 13:18:26 +0000 |
77 | @@ -1,3 +1,45 @@ |
78 | +indicator-sound (12.10.2+15.04.20150812.3-0ubuntu1) vivid; urgency=medium |
79 | + |
80 | + [ CI Train Bot ] |
81 | + * New rebuild forced. |
82 | + |
83 | + [ Charles Kerr ] |
84 | + * When showing a "Loud volumes can damage your hearing" confirmation |
85 | + warning, clamp the volume until the user hits "OK". (LP: #1481913) |
86 | + |
87 | + -- CI Train Bot <ci-train-bot@canonical.com> Wed, 12 Aug 2015 20:55:05 +0000 |
88 | + |
89 | +indicator-sound (12.10.2+15.04.20150807.6-0ubuntu1) vivid; urgency=medium |
90 | + |
91 | + [ CI Train Bot ] |
92 | + * New rebuild forced. |
93 | + |
94 | + [ Charles Kerr ] |
95 | + * Revised UI volume warnings to comply with EU requirements. (LP: |
96 | + #1481913) |
97 | + |
98 | + -- CI Train Bot <ci-train-bot@canonical.com> Fri, 07 Aug 2015 22:35:58 +0000 |
99 | + |
100 | +indicator-sound (12.10.2+15.04.20150605-0ubuntu1) vivid; urgency=medium |
101 | + |
102 | + * |
103 | + |
104 | + -- CI Train Bot <ci-train-bot@canonical.com> Fri, 05 Jun 2015 19:50:06 +0000 |
105 | + |
106 | +indicator-sound (12.10.2+15.04.20150507+eventually4-0ubuntu1) vivid; urgency=medium |
107 | + |
108 | + * Using eventually to avoid arbitrary timeouts in tests |
109 | + |
110 | + -- Ted Gould <ted@ubuntu.com> Wed, 27 May 2015 11:11:19 -0500 |
111 | + |
112 | +indicator-sound (12.10.2+15.04.20150507-0ubuntu1) vivid; urgency=medium |
113 | + |
114 | + [ Charles Kerr ] |
115 | + * Use the BusWatcher to look for org.freedesktop.Notifications |
116 | + ownership changes on the bus. (LP: #1432446) |
117 | + |
118 | + -- CI Train Bot <ci-train-bot@canonical.com> Thu, 07 May 2015 15:27:00 +0000 |
119 | + |
120 | indicator-sound (12.10.2+15.04.20150508-0ubuntu1) vivid; urgency=medium |
121 | |
122 | [ CI Train Bot ] |
123 | |
124 | === modified file 'src/service.vala' |
125 | --- src/service.vala 2015-04-20 21:02:32 +0000 |
126 | +++ src/service.vala 2015-10-13 13:18:26 +0000 |
127 | @@ -20,6 +20,13 @@ |
128 | public class IndicatorSound.Service: Object { |
129 | DBusConnection bus; |
130 | |
131 | + /** |
132 | + * A copy of volume_control.volume made before just warn_notification |
133 | + * is shown. Since the volume is clamped during the warning, we cache |
134 | + * the previous volume to use iff the user hits "OK". |
135 | + */ |
136 | + VolumeControl.Volume _pre_warn_volume = null; |
137 | + |
138 | public Service (MediaPlayerList playerlist, VolumeControl volume, AccountsServiceUser? accounts) { |
139 | try { |
140 | bus = Bus.get_sync(GLib.BusType.SESSION); |
141 | @@ -27,14 +34,19 @@ |
142 | error("Unable to get DBus session bus: %s", e.message); |
143 | } |
144 | |
145 | - sync_notification = new Notify.Notification(_("Volume"), "", "audio-volume-muted"); |
146 | + info_notification = new Notify.Notification(_("Volume"), "", "audio-volume-muted"); |
147 | + |
148 | + warn_notification = new Notify.Notification(_("Volume"), _("High volume can damage your hearing."), "audio-volume-high"); |
149 | + warn_notification.set_hint ("x-canonical-non-shaped-icon", "true"); |
150 | + warn_notification.set_hint ("x-canonical-snap-decisions", "true"); |
151 | + warn_notification.set_hint ("x-canonical-private-affirmative-tint", "true"); |
152 | + warn_notification.closed.connect((n) => { n.clear_actions(); }); |
153 | BusWatcher.watch_namespace (GLib.BusType.SESSION, |
154 | "org.freedesktop.Notifications", |
155 | - () => { debug("Notifications name appeared"); check_sync_notification = false; }, |
156 | - () => { debug("Notifications name vanshed"); check_sync_notification = false; }); |
157 | + () => { debug("Notifications name appeared"); notify_server_caps_checked = false; }, |
158 | + () => { debug("Notifications name vanshed"); notify_server_caps_checked = false; }); |
159 | |
160 | this.settings = new Settings ("com.canonical.indicator.sound"); |
161 | - this.sharedsettings = new Settings ("com.ubuntu.sound"); |
162 | |
163 | this.settings.bind ("visible", this, "visible", SettingsBindFlags.GET); |
164 | this.notify["visible"].connect ( () => this.update_root_icon () ); |
165 | @@ -83,19 +95,13 @@ |
166 | this.sync_preferred_players (); |
167 | }); |
168 | |
169 | - sharedsettings.bind ("allow-amplified-volume", this, "allow-amplified-volume", SettingsBindFlags.GET); |
170 | - |
171 | /* Hide the notification when the menu is shown */ |
172 | var shown_action = actions.lookup_action ("indicator-shown") as SimpleAction; |
173 | shown_action.change_state.connect ((state) => { |
174 | - block_notifications = state.get_boolean(); |
175 | - if (block_notifications) { |
176 | + block_info_notifications = state.get_boolean(); |
177 | + if (block_info_notifications) { |
178 | debug("Indicator is shown"); |
179 | - try { |
180 | - sync_notification.close(); |
181 | - } catch (Error e) { |
182 | - warning("Unable to close synchronous volume notification: %s", e.message); |
183 | - } |
184 | + close_notification(info_notification); |
185 | } else { |
186 | debug("Indicator is hidden"); |
187 | } |
188 | @@ -111,11 +117,33 @@ |
189 | this.menus.@foreach ( (profile, menu) => menu.export (bus, @"/com/canonical/indicator/sound/$profile")); |
190 | } |
191 | |
192 | + private void close_notification(Notify.Notification? n) { |
193 | + return_if_fail (n != null); |
194 | + if (n.id != 0) { |
195 | + try { |
196 | + n.close(); |
197 | + } catch (GLib.Error e) { |
198 | + warning("Unable to close notification: %s", e.message); |
199 | + } |
200 | + } |
201 | + } |
202 | + |
203 | + private void show_notification(Notify.Notification? n) { |
204 | + return_if_fail (n != null); |
205 | + try { |
206 | + n.show (); |
207 | + } catch (GLib.Error e) { |
208 | + warning ("Unable to show notification: %s", e.message); |
209 | + } |
210 | + } |
211 | + |
212 | ~Service() { |
213 | debug("Destroying Service Object"); |
214 | |
215 | clear_acts_player(); |
216 | |
217 | + stop_clamp_to_high_timeout(); |
218 | + |
219 | if (this.player_action_update_id > 0) { |
220 | Source.remove (this.player_action_update_id); |
221 | this.player_action_update_id = 0; |
222 | @@ -145,28 +173,6 @@ |
223 | |
224 | public bool visible { get; set; } |
225 | |
226 | - public bool allow_amplified_volume { |
227 | - get { |
228 | - return this.max_volume > 1.0; |
229 | - } |
230 | - |
231 | - set { |
232 | - if (this.allow_amplified_volume == value) |
233 | - return; |
234 | - |
235 | - if (value) { |
236 | - /* from pulse/volume.h: #define PA_VOLUME_UI_MAX (pa_sw_volume_from_dB(+11.0)) */ |
237 | - this.max_volume = (double)PulseAudio.Volume.sw_from_dB(11.0) / PulseAudio.Volume.NORM; |
238 | - } |
239 | - else { |
240 | - this.max_volume = 1.0; |
241 | - } |
242 | - |
243 | - /* Normalize volume, because the volume action's state is [0.0, 1.0], see create_volume_action() */ |
244 | - this.actions.change_action_state ("volume", this.volume_control.volume.volume / this.max_volume); |
245 | - } |
246 | - } |
247 | - |
248 | const ActionEntry[] action_entries = { |
249 | { "root", null, null, "@a{sv} {}", null }, |
250 | { "scroll", activate_scroll_action, "i", null, null }, |
251 | @@ -178,7 +184,6 @@ |
252 | SimpleActionGroup actions; |
253 | HashTable<string, SoundMenu> menus; |
254 | Settings settings; |
255 | - Settings sharedsettings; |
256 | VolumeControl volume_control; |
257 | MediaPlayerList players; |
258 | uint player_action_update_id; |
259 | @@ -187,28 +192,27 @@ |
260 | bool syncing_preferred_players = false; |
261 | AccountsServiceUser? accounts_service = null; |
262 | bool export_to_accounts_service = false; |
263 | - private Notify.Notification sync_notification; |
264 | - |
265 | - /* Maximum volume as a scaling factor between the volume action's state and the value in |
266 | - * this.volume_control. See create_volume_action(). |
267 | - */ |
268 | - double max_volume = 1.0; |
269 | + private Notify.Notification info_notification; |
270 | + private Notify.Notification warn_notification; |
271 | |
272 | const double volume_step_percentage = 0.06; |
273 | |
274 | - void activate_scroll_action (SimpleAction action, Variant? param) { |
275 | + private void activate_scroll_action (SimpleAction action, Variant? param) { |
276 | int delta = param.get_int32(); /* positive for up, negative for down */ |
277 | - |
278 | - var scrollvol = new VolumeControl.Volume(); |
279 | - double v = this.volume_control.volume.volume + volume_step_percentage * delta; |
280 | - scrollvol.volume = v.clamp (0.0, this.max_volume); |
281 | - scrollvol.reason = VolumeControl.VolumeReasons.USER_KEYPRESS; |
282 | - this.volume_control.volume = scrollvol; |
283 | + double v = volume_control.volume.volume + volume_step_percentage * delta; |
284 | + volume_control.set_volume_clamp (v, VolumeControl.VolumeReasons.USER_KEYPRESS); |
285 | } |
286 | |
287 | void activate_desktop_settings (SimpleAction action, Variant? param) { |
288 | var env = Environment.get_variable ("DESKTOP_SESSION"); |
289 | string cmd; |
290 | + |
291 | + if (Environment.get_variable ("MIR_SOCKET") != null) |
292 | + { |
293 | + UrlDispatch.send ("settings:///system/sound"); |
294 | + return; |
295 | + } |
296 | + |
297 | if (env == "xubuntu" || env == "ubuntustudio") |
298 | cmd = "pavucontrol"; |
299 | else if (env == "mate") |
300 | @@ -242,12 +246,10 @@ |
301 | void update_root_icon () { |
302 | double volume = this.volume_control.volume.volume; |
303 | string icon; |
304 | - if (this.volume_control.mute) |
305 | + if (this.volume_control.mute || volume <= 0.0) |
306 | icon = this.mute_blocks_sound ? "audio-volume-muted-blocking-panel" : "audio-volume-muted-panel"; |
307 | else if (this.accounts_service != null && this.accounts_service.silentMode) |
308 | icon = "audio-volume-muted-panel"; |
309 | - else if (volume <= 0.0) |
310 | - icon = "audio-volume-low-zero-panel"; |
311 | else if (volume <= 0.3) |
312 | icon = "audio-volume-low-panel"; |
313 | else if (volume <= 0.7) |
314 | @@ -275,60 +277,83 @@ |
315 | root_action.set_state (builder.end()); |
316 | } |
317 | |
318 | - private bool check_sync_notification = false; |
319 | - private bool support_sync_notification = false; |
320 | - private bool block_notifications = false; |
321 | - |
322 | - void update_sync_notification () { |
323 | - if (!check_sync_notification) { |
324 | - support_sync_notification = false; |
325 | + private bool notify_server_caps_checked = false; |
326 | + private bool notify_server_supports_actions = false; |
327 | + private bool notify_server_supports_sync = false; |
328 | + private bool block_info_notifications = false; |
329 | + |
330 | + private void update_notification () { |
331 | + |
332 | + if (!notify_server_caps_checked) { |
333 | List<string> caps = Notify.get_server_caps (); |
334 | - if (caps.find_custom ("x-canonical-private-synchronous", strcmp) != null) { |
335 | - support_sync_notification = true; |
336 | - } |
337 | - check_sync_notification = true; |
338 | + notify_server_supports_actions = caps.find_custom ("actions", strcmp) != null; |
339 | + notify_server_supports_sync = caps.find_custom ("x-canonical-private-synchronous", strcmp) != null; |
340 | + notify_server_caps_checked = true; |
341 | } |
342 | |
343 | - if (!support_sync_notification) |
344 | - return; |
345 | - |
346 | - if (block_notifications) |
347 | - return; |
348 | - |
349 | - /* Determine Label */ |
350 | - string volume_label = ""; |
351 | - if (volume_control.high_volume) |
352 | - volume_label = _("High volume"); |
353 | - |
354 | - /* Choose an icon */ |
355 | - string icon = "audio-volume-muted"; |
356 | - if (volume_control.volume.volume <= 0.0) |
357 | - icon = "audio-volume-muted"; |
358 | - else if (volume_control.volume.volume <= 0.3) |
359 | - icon = "audio-volume-low"; |
360 | - else if (volume_control.volume.volume <= 0.7) |
361 | - icon = "audio-volume-medium"; |
362 | - else |
363 | - icon = "audio-volume-high"; |
364 | - |
365 | - /* Check tint */ |
366 | - string tint = "false"; |
367 | - if (volume_control.high_volume) |
368 | - tint = "true"; |
369 | - |
370 | - /* Put it all into the notification */ |
371 | - sync_notification.clear_hints (); |
372 | - sync_notification.update (_("Volume"), volume_label, icon); |
373 | - sync_notification.set_hint ("value", (int32)Math.round(volume_control.volume.volume / this.max_volume * 100.0)); |
374 | - sync_notification.set_hint ("x-canonical-value-bar-tint", tint); |
375 | - sync_notification.set_hint ("x-canonical-private-synchronous", "true"); |
376 | - sync_notification.set_hint ("x-canonical-non-shaped-icon", "true"); |
377 | - |
378 | - /* Show it */ |
379 | - try { |
380 | - sync_notification.show (); |
381 | - } catch (GLib.Error e) { |
382 | - warning("Unable to send volume change notification: %s", e.message); |
383 | + var loud = volume_control.high_volume; |
384 | + var warn = loud |
385 | + && this.notify_server_supports_actions |
386 | + && !this.volume_control.high_volume_approved; |
387 | + |
388 | + if (warn) { |
389 | + close_notification(info_notification); |
390 | + if (_pre_warn_volume == null) { |
391 | + _pre_warn_volume = new VolumeControl.Volume(); |
392 | + _pre_warn_volume.volume = volume_control.volume.volume; |
393 | + _pre_warn_volume.reason = volume_control.volume.reason; |
394 | + } |
395 | + warn_notification.clear_actions(); |
396 | + warn_notification.add_action ("ok", _("OK"), (n, a) => { |
397 | + stop_clamp_to_high_timeout(); |
398 | + volume_control.approve_high_volume (); |
399 | + if (_pre_warn_volume != null) { |
400 | + var tmp = _pre_warn_volume; |
401 | + _pre_warn_volume = null; |
402 | + volume_control.volume = tmp; |
403 | + } |
404 | + }); |
405 | + warn_notification.add_action ("cancel", _("Cancel"), (n, a) => { |
406 | + _pre_warn_volume = null; |
407 | + }); |
408 | + show_notification(warn_notification); |
409 | + } else { |
410 | + close_notification(warn_notification); |
411 | + |
412 | + if (notify_server_supports_sync && !block_info_notifications) { |
413 | + |
414 | + /* Determine Label */ |
415 | + unowned string volume_label = loud |
416 | + ? _("High volume can damage your hearing.") |
417 | + : ""; |
418 | + |
419 | + /* Choose an icon */ |
420 | + unowned string icon; |
421 | + if (loud) { |
422 | + icon = "audio-volume-high"; |
423 | + } else { |
424 | + var volume = volume_control.volume.volume; |
425 | + |
426 | + if (volume <= 0.0) |
427 | + icon = "audio-volume-muted"; |
428 | + else if (volume <= 0.3) |
429 | + icon = "audio-volume-low"; |
430 | + else if (volume <= 0.7) |
431 | + icon = "audio-volume-medium"; |
432 | + else |
433 | + icon = "audio-volume-high"; |
434 | + } |
435 | + |
436 | + /* Reset the notification */ |
437 | + var n = this.info_notification; |
438 | + n.update (_("Volume"), volume_label, icon); |
439 | + n.clear_hints(); |
440 | + n.set_hint ("x-canonical-non-shaped-icon", "true"); |
441 | + n.set_hint ("x-canonical-private-synchronous", "true"); |
442 | + n.set_hint ("x-canonical-value-bar-tint", loud ? "true" : "false"); |
443 | + n.set_hint ("value", (int32)Math.round(get_volume_percent() * 100.0)); |
444 | + show_notification(n); |
445 | + } |
446 | } |
447 | } |
448 | |
449 | @@ -408,49 +433,49 @@ |
450 | return mute_action; |
451 | } |
452 | |
453 | - SimpleAction volume_action; |
454 | - Action create_volume_action () { |
455 | - /* The action's state is between be in [0.0, 1.0] instead of [0.0, |
456 | - * max_volume], so that we don't need to update the slider menu item |
457 | - * every time allow-amplified-volume is changed. Convert between the |
458 | - * two here, so that we always pass the full range into |
459 | - * volume_control.set_volume(). |
460 | - */ |
461 | - |
462 | - double volume = this.volume_control.volume.volume / this.max_volume; |
463 | - |
464 | - volume_action = new SimpleAction.stateful ("volume", VariantType.INT32, new Variant.double (volume)); |
465 | + /* return the current volume in the range of [0.0, 1.0] */ |
466 | + private double get_volume_percent() { |
467 | + return volume_control.volume.volume / this.volume_control.max_volume; |
468 | + } |
469 | + |
470 | + /* volume control's range can vary depending on its max_volume property, |
471 | + * but the action always needs to be in [0.0, 1.0]... */ |
472 | + private Variant create_volume_action_state() { |
473 | + return new Variant.double (get_volume_percent()); |
474 | + } |
475 | + |
476 | + private void update_volume_action_state() { |
477 | + volume_action.set_state(create_volume_action_state()); |
478 | + } |
479 | + |
480 | + private SimpleAction volume_action; |
481 | + private Action create_volume_action () { |
482 | + volume_action = new SimpleAction.stateful ("volume", VariantType.INT32, create_volume_action_state()); |
483 | |
484 | volume_action.change_state.connect ( (action, val) => { |
485 | - double v = val.get_double () * this.max_volume; |
486 | - |
487 | - var vol = new VolumeControl.Volume(); |
488 | - vol.volume = v.clamp (0.0, this.max_volume); |
489 | - vol.reason = VolumeControl.VolumeReasons.USER_KEYPRESS; |
490 | - volume_control.volume = vol; |
491 | + double v = val.get_double () * this.volume_control.max_volume; |
492 | + volume_control.set_volume_clamp (v, VolumeControl.VolumeReasons.USER_KEYPRESS); |
493 | }); |
494 | |
495 | /* activating this action changes the volume by the amount given in the parameter */ |
496 | - volume_action.activate.connect ( (action, param) => { |
497 | - int delta = param.get_int32 (); |
498 | - double v = volume_control.volume.volume + volume_step_percentage * delta; |
499 | + volume_action.activate.connect ((a,p) => activate_scroll_action(a,p)); |
500 | |
501 | - var vol = new VolumeControl.Volume(); |
502 | - vol.volume = v.clamp (0.0, this.max_volume); |
503 | - vol.reason = VolumeControl.VolumeReasons.USER_KEYPRESS; |
504 | - volume_control.volume = vol; |
505 | + this.volume_control.notify["max-volume"].connect(() => { |
506 | + update_volume_action_state(); |
507 | }); |
508 | |
509 | this.volume_control.notify["volume"].connect (() => { |
510 | - /* Normalize volume, because the volume action's state is [0.0, 1.0], see create_volume_action() */ |
511 | - volume_action.set_state (new Variant.double (this.volume_control.volume.volume / this.max_volume)); |
512 | + update_volume_action_state(); |
513 | |
514 | this.update_root_icon (); |
515 | |
516 | var reason = volume_control.volume.reason; |
517 | if (reason == VolumeControl.VolumeReasons.USER_KEYPRESS || |
518 | reason == VolumeControl.VolumeReasons.DEVICE_OUTPUT_CHANGE) |
519 | - this.update_sync_notification (); |
520 | + this.update_notification (); |
521 | + |
522 | + if ((warn_notification.id != 0) && (_pre_warn_volume != null)) |
523 | + clamp_to_high_soon(); |
524 | }); |
525 | |
526 | this.volume_control.bind_property ("ready", volume_action, "enabled", BindingFlags.SYNC_CREATE); |
527 | @@ -481,7 +506,7 @@ |
528 | |
529 | this.volume_control.notify["high-volume"].connect( () => { |
530 | high_volume_action.set_state(new Variant.boolean (this.volume_control.high_volume)); |
531 | - update_sync_notification(); |
532 | + update_notification(); |
533 | }); |
534 | |
535 | return high_volume_action; |
536 | @@ -604,4 +629,27 @@ |
537 | |
538 | this.update_preferred_players (); |
539 | } |
540 | + |
541 | + /** VOLUME CLAMPING **/ |
542 | + |
543 | + private uint _clamp_to_high_timeout = 0; |
544 | + |
545 | + private void stop_clamp_to_high_timeout() { |
546 | + if (_clamp_to_high_timeout != 0) { |
547 | + Source.remove(_clamp_to_high_timeout); |
548 | + _clamp_to_high_timeout = 0; |
549 | + } |
550 | + } |
551 | + |
552 | + private void clamp_to_high_soon() { |
553 | + const uint interval_msec = 200; |
554 | + if (_clamp_to_high_timeout == 0) |
555 | + _clamp_to_high_timeout = Timeout.add(interval_msec, clamp_to_high_idle); |
556 | + } |
557 | + |
558 | + private bool clamp_to_high_idle() { |
559 | + _clamp_to_high_timeout = 0; |
560 | + volume_control.clamp_to_high_volume(); |
561 | + return false; // Source.REMOVE; |
562 | + } |
563 | } |
564 | |
565 | === modified file 'src/volume-control-pulse.vala' |
566 | --- src/volume-control-pulse.vala 2015-04-17 17:10:28 +0000 |
567 | +++ src/volume-control-pulse.vala 2015-10-13 13:18:26 +0000 |
568 | @@ -28,8 +28,8 @@ |
569 | [DBus (name="com.canonical.UnityGreeter.List")] |
570 | interface GreeterListInterface : Object |
571 | { |
572 | - public abstract async string get_active_entry () throws IOError; |
573 | - public signal void entry_selected (string entry_name); |
574 | + public abstract async string get_active_entry () throws IOError; |
575 | + public signal void entry_selected (string entry_name); |
576 | } |
577 | |
578 | public class VolumeControlPulse : VolumeControl |
579 | @@ -44,6 +44,8 @@ |
580 | private bool _is_playing = false; |
581 | private VolumeControl.Volume _volume = new VolumeControl.Volume(); |
582 | private double _mic_volume = 0.0; |
583 | + private Settings _settings = new Settings ("com.canonical.indicator.sound"); |
584 | + private Settings _shared_settings = new Settings ("com.ubuntu.sound"); |
585 | |
586 | /* Used by the pulseaudio stream restore extension */ |
587 | private DBusConnection _pconn; |
588 | @@ -92,13 +94,6 @@ |
589 | /** true when a microphone is active **/ |
590 | public override bool active_mic { get; private set; default = false; } |
591 | |
592 | - /** true when high volume warnings should be shown */ |
593 | - public override bool high_volume { |
594 | - get { |
595 | - return this._volume.volume > 0.75 && _active_port_headphone && stream == "multimedia"; |
596 | - } |
597 | - } |
598 | - |
599 | public VolumeControlPulse () |
600 | { |
601 | _volume.volume = 0.0; |
602 | @@ -110,19 +105,34 @@ |
603 | _mute_cancellable = new Cancellable (); |
604 | _volume_cancellable = new Cancellable (); |
605 | |
606 | + init_all_properties(); |
607 | + |
608 | setup_accountsservice.begin (); |
609 | |
610 | this.reconnect_to_pulse (); |
611 | } |
612 | |
613 | + private void init_all_properties() |
614 | + { |
615 | + init_max_volume(); |
616 | + init_high_volume(); |
617 | + init_high_volume_approved(); |
618 | + } |
619 | + |
620 | ~VolumeControlPulse () |
621 | { |
622 | + stop_all_timers(); |
623 | + } |
624 | + |
625 | + private void stop_all_timers() |
626 | + { |
627 | if (_reconnect_timer != 0) { |
628 | Source.remove (_reconnect_timer); |
629 | _reconnect_timer = 0; |
630 | } |
631 | stop_local_volume_timer(); |
632 | stop_account_service_volume_timer(); |
633 | + stop_high_volume_approved_timer(); |
634 | } |
635 | |
636 | /* PulseAudio logic*/ |
637 | @@ -212,7 +222,7 @@ |
638 | vol.volume = volume_to_double (i.volume.max ()); |
639 | vol.reason = VolumeControl.VolumeReasons.PULSE_CHANGE; |
640 | this.volume = vol; |
641 | - } |
642 | + } |
643 | } |
644 | |
645 | private void source_info_cb (Context c, SourceInfo? i, int eol) |
646 | @@ -619,7 +629,6 @@ |
647 | var volume_changed = (value.volume != _volume.volume); |
648 | debug("Setting volume to %f for profile %d because %d", value.volume, _active_sink_input, value.reason); |
649 | |
650 | - var old_high_volume = this.high_volume; |
651 | _volume = value; |
652 | |
653 | /* Make sure we're connected to Pulse and pulse didn't give us the change */ |
654 | @@ -631,15 +640,153 @@ |
655 | else |
656 | context.get_server_info (server_info_cb_for_set_volume); |
657 | |
658 | - if (this.high_volume != old_high_volume) |
659 | - this.notify_property("high-volume"); |
660 | |
661 | if (volume.reason != VolumeControl.VolumeReasons.ACCOUNTS_SERVICE_SET |
662 | && volume_changed) { |
663 | start_local_volume_timer(); |
664 | } |
665 | - } |
666 | - } |
667 | + |
668 | + update_high_volume(); |
669 | + } |
670 | + } |
671 | + |
672 | + /** MAX VOLUME PROPERTY **/ |
673 | + |
674 | + private void init_max_volume() { |
675 | + _settings.changed["normal-volume-decibels"].connect(() => update_max_volume()); |
676 | + _settings.changed["amplified-volume-decibels"].connect(() => update_max_volume()); |
677 | + _shared_settings.changed["allow-amplified-volume"].connect(() => update_max_volume()); |
678 | + update_max_volume(); |
679 | + } |
680 | + private void update_max_volume () { |
681 | + var new_max_volume = calculate_max_volume(); |
682 | + if (max_volume != new_max_volume) { |
683 | + debug("changing max_volume from %f to %f", this.max_volume, new_max_volume); |
684 | + max_volume = calculate_max_volume(); |
685 | + } |
686 | + } |
687 | + private double calculate_max_volume () { |
688 | + unowned string decibel_key = _shared_settings.get_boolean("allow-amplified-volume") |
689 | + ? "amplified-volume-decibels" |
690 | + : "normal-volume-decibels"; |
691 | + var volume_dB = _settings.get_double(decibel_key); |
692 | + var volume_sw = PulseAudio.Volume.sw_from_dB (volume_dB); |
693 | + return volume_to_double (volume_sw); |
694 | + } |
695 | + |
696 | + /** HIGH VOLUME PROPERTY **/ |
697 | + |
698 | + private bool _warning_volume_enabled; |
699 | + private double _warning_volume_norms; /* 1.0 == PA_VOLUME_NORM */ |
700 | + private bool _high_volume = false; |
701 | + public override bool high_volume { |
702 | + get { return this._high_volume; } |
703 | + private set { this._high_volume = value; } |
704 | + } |
705 | + private void init_high_volume() { |
706 | + _settings.changed["warning-volume-enabled"].connect(() => update_high_volume_cache()); |
707 | + _settings.changed["warning-volume-decibels"].connect(() => update_high_volume_cache()); |
708 | + update_high_volume_cache(); |
709 | + } |
710 | + private void update_high_volume_cache() { |
711 | + var volume_dB = _settings.get_double ("warning-volume-decibels"); |
712 | + var volume_sw = PulseAudio.Volume.sw_from_dB (volume_dB); |
713 | + var volume_norms = volume_to_double (volume_sw); |
714 | + _warning_volume_norms = volume_norms; |
715 | + _warning_volume_enabled = _settings.get_boolean("warning-volume-enabled"); |
716 | + debug("updating high volume cache... enabled %d dB %f sw %lu norm %f", (int)_warning_volume_enabled, volume_dB, volume_sw, volume_norms); |
717 | + update_high_volume(); |
718 | + } |
719 | + private void update_high_volume() { |
720 | + var new_high_volume = calculate_high_volume(); |
721 | + if (high_volume != new_high_volume) { |
722 | + debug("changing high_volume from %d to %d", (int)high_volume, (int)new_high_volume); |
723 | + high_volume = new_high_volume; |
724 | + } |
725 | + } |
726 | + private bool calculate_high_volume() { |
727 | + return calculate_high_volume_from_volume(_volume.volume); |
728 | + } |
729 | + private bool calculate_high_volume_from_volume(double volume) { |
730 | + return _active_port_headphone |
731 | + && _warning_volume_enabled |
732 | + && volume >= _warning_volume_norms |
733 | + && (stream == "multimedia"); |
734 | + } |
735 | + |
736 | + public override void clamp_to_high_volume() { |
737 | + if (_high_volume && (_volume.volume > _warning_volume_norms)) { |
738 | + var vol = new VolumeControl.Volume(); |
739 | + vol.volume = _volume.volume.clamp(0, _warning_volume_norms); |
740 | + vol.reason = _volume.reason; |
741 | + debug("Clamping from %f down to %f", _volume.volume, vol.volume); |
742 | + volume = vol; |
743 | + } |
744 | + } |
745 | + |
746 | + /** HIGH VOLUME APPROVED PROPERTY **/ |
747 | + |
748 | + private bool _high_volume_approved = false; |
749 | + private uint _high_volume_approved_timer = 0; |
750 | + private int64 _high_volume_approved_at = 0; |
751 | + private int64 _high_volume_approved_ttl_usec = 0; |
752 | + public override bool high_volume_approved { |
753 | + get { return this._high_volume_approved; } |
754 | + private set { this._high_volume_approved = value; } |
755 | + } |
756 | + private void init_high_volume_approved() { |
757 | + _settings.changed["warning-volume-confirmation-ttl"].connect(() => update_high_volume_approved_cache()); |
758 | + update_high_volume_approved_cache(); |
759 | + } |
760 | + private void update_high_volume_approved_cache() { |
761 | + _high_volume_approved_ttl_usec = _settings.get_int("warning-volume-confirmation-ttl"); |
762 | + _high_volume_approved_ttl_usec *= 1000000; |
763 | + |
764 | + update_high_volume_approved(); |
765 | + update_high_volume_approved_timer(); |
766 | + } |
767 | + private void update_high_volume_approved_timer() { |
768 | + stop_high_volume_approved_timer(); |
769 | + if (_high_volume_approved_at != 0) { |
770 | + int64 expires_at = _high_volume_approved_at + _high_volume_approved_ttl_usec; |
771 | + int64 now = GLib.get_monotonic_time(); |
772 | + if (expires_at > now) { |
773 | + var seconds_left = 1 + ((expires_at - now) / 1000000); |
774 | + _high_volume_approved_timer = Timeout.add_seconds((uint)seconds_left, on_high_volume_approved_timer); |
775 | + } |
776 | + } |
777 | + } |
778 | + private void stop_high_volume_approved_timer() { |
779 | + if (_high_volume_approved_timer != 0) { |
780 | + Source.remove (_high_volume_approved_timer); |
781 | + _high_volume_approved_timer = 0; |
782 | + } |
783 | + } |
784 | + private bool on_high_volume_approved_timer() { |
785 | + _high_volume_approved_timer = 0; |
786 | + update_high_volume_approved(); |
787 | + return false; /* Source.REMOVE */ |
788 | + } |
789 | + private void update_high_volume_approved() { |
790 | + var new_high_volume_approved = calculate_high_volume_approved(); |
791 | + if (high_volume_approved != new_high_volume_approved) { |
792 | + debug("changing high_volume_approved from %d to %d", (int)high_volume_approved, (int)new_high_volume_approved); |
793 | + high_volume_approved = new_high_volume_approved; |
794 | + } |
795 | + } |
796 | + private bool calculate_high_volume_approved() { |
797 | + int64 now = GLib.get_monotonic_time(); |
798 | + return (_high_volume_approved_at != 0) |
799 | + && (_high_volume_approved_at + _high_volume_approved_ttl_usec >= now); |
800 | + } |
801 | + public override void approve_high_volume() { |
802 | + _high_volume_approved_at = GLib.get_monotonic_time(); |
803 | + update_high_volume_approved(); |
804 | + update_high_volume_approved_timer(); |
805 | + } |
806 | + |
807 | + |
808 | + /** MIC VOLUME PROPERTY */ |
809 | |
810 | public override double mic_volume { |
811 | get { |
812 | |
813 | === modified file 'src/volume-control.vala' |
814 | --- src/volume-control.vala 2015-02-17 16:54:33 +0000 |
815 | +++ src/volume-control.vala 2015-10-13 13:18:26 +0000 |
816 | @@ -36,12 +36,24 @@ |
817 | public virtual string stream { get { return ""; } } |
818 | public virtual bool ready { get { return false; } set { } } |
819 | public virtual bool active_mic { get { return false; } set { } } |
820 | - public virtual bool high_volume { get { return false; } } |
821 | + public virtual bool high_volume { get { return false; } protected set { } } |
822 | public virtual bool mute { get { return false; } } |
823 | public virtual bool is_playing { get { return false; } } |
824 | private Volume _volume; |
825 | public virtual Volume volume { get { return _volume; } set { } } |
826 | public virtual double mic_volume { get { return 0.0; } set { } } |
827 | + public virtual double max_volume { get { return 1.0; } protected set { } } |
828 | + |
829 | + public virtual bool high_volume_approved { get { return false; } protected set { } } |
830 | + public virtual void approve_high_volume() { } |
831 | + public virtual void clamp_to_high_volume() { } |
832 | |
833 | public abstract void set_mute (bool mute); |
834 | + |
835 | + public void set_volume_clamp (double unclamped, VolumeControl.VolumeReasons reason) { |
836 | + var v = new VolumeControl.Volume(); |
837 | + v.volume = unclamped.clamp (0.0, this.max_volume); |
838 | + v.reason = reason; |
839 | + this.volume = v; |
840 | + } |
841 | } |
842 | |
843 | === modified file 'tests/CMakeLists.txt' |
844 | --- tests/CMakeLists.txt 2015-02-09 22:10:22 +0000 |
845 | +++ tests/CMakeLists.txt 2015-10-13 13:18:26 +0000 |
846 | @@ -163,7 +163,7 @@ |
847 | ########################### |
848 | |
849 | include_directories(${CMAKE_SOURCE_DIR}/src) |
850 | -add_executable (volume-control-test volume-control-test.cc) |
851 | +add_executable (volume-control-test volume-control-test.cc gschemas.compiled) |
852 | target_link_libraries ( |
853 | volume-control-test |
854 | indicator-sound-service-lib |
855 | |
856 | === modified file 'tests/media-player-user.cc' |
857 | --- tests/media-player-user.cc 2015-01-30 16:05:10 +0000 |
858 | +++ tests/media-player-user.cc 2015-10-13 13:18:26 +0000 |
859 | @@ -17,6 +17,9 @@ |
860 | * Ted Gould <ted@canonical.com> |
861 | */ |
862 | |
863 | +#include <chrono> |
864 | +#include <future> |
865 | + |
866 | #include <gtest/gtest.h> |
867 | #include <gio/gio.h> |
868 | #include <libdbustest/dbus-test.h> |
869 | @@ -31,24 +34,55 @@ |
870 | { |
871 | |
872 | protected: |
873 | - DbusTestService * service = NULL; |
874 | + DbusTestService * testsystem = NULL; |
875 | AccountsServiceMock service_mock; |
876 | |
877 | + DbusTestService * testsession = NULL; |
878 | + |
879 | + DbusTestProcess * systemmonitor = nullptr; |
880 | + DbusTestProcess * sessionmonitor = nullptr; |
881 | + |
882 | GDBusConnection * system = NULL; |
883 | + GDBusConnection * session = NULL; |
884 | GDBusProxy * proxy = NULL; |
885 | |
886 | + std::chrono::milliseconds _eventuallyTime = std::chrono::seconds{5}; |
887 | + |
888 | virtual void SetUp() { |
889 | - service = dbus_test_service_new(NULL); |
890 | - dbus_test_service_set_bus(service, DBUS_TEST_SERVICE_BUS_SYSTEM); |
891 | - |
892 | - dbus_test_service_add_task(service, (DbusTestTask*)service_mock); |
893 | - dbus_test_service_start_tasks(service); |
894 | + /* System Bus */ |
895 | + testsystem = dbus_test_service_new(NULL); |
896 | + dbus_test_service_set_bus(testsystem, DBUS_TEST_SERVICE_BUS_SYSTEM); |
897 | + |
898 | + systemmonitor = dbus_test_process_new("dbus-monitor"); |
899 | + dbus_test_process_append_param(systemmonitor, "--system"); |
900 | + dbus_test_task_set_name(DBUS_TEST_TASK(systemmonitor), "System"); |
901 | + dbus_test_service_add_task(testsystem, DBUS_TEST_TASK(systemmonitor)); |
902 | + |
903 | + dbus_test_service_add_task(testsystem, (DbusTestTask*)service_mock); |
904 | + dbus_test_service_start_tasks(testsystem); |
905 | |
906 | system = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, NULL); |
907 | ASSERT_NE(nullptr, system); |
908 | g_dbus_connection_set_exit_on_close(system, FALSE); |
909 | g_object_add_weak_pointer(G_OBJECT(system), (gpointer *)&system); |
910 | |
911 | + /* Session Bus */ |
912 | + testsession = dbus_test_service_new(NULL); |
913 | + dbus_test_service_set_bus(testsession, DBUS_TEST_SERVICE_BUS_SESSION); |
914 | + |
915 | + sessionmonitor = dbus_test_process_new("dbus-monitor"); |
916 | + dbus_test_process_append_param(sessionmonitor, "--session"); |
917 | + dbus_test_task_set_name(DBUS_TEST_TASK(sessionmonitor), "Session"); |
918 | + dbus_test_service_add_task(testsession, DBUS_TEST_TASK(sessionmonitor)); |
919 | + |
920 | + dbus_test_service_start_tasks(testsession); |
921 | + |
922 | + session = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, NULL); |
923 | + ASSERT_NE(nullptr, session); |
924 | + g_dbus_connection_set_exit_on_close(session, FALSE); |
925 | + g_object_add_weak_pointer(G_OBJECT(session), (gpointer *)&session); |
926 | + |
927 | + /* Setup proxy */ |
928 | proxy = g_dbus_proxy_new_sync(system, |
929 | G_DBUS_PROXY_FLAGS_NONE, |
930 | NULL, |
931 | @@ -60,10 +94,15 @@ |
932 | } |
933 | |
934 | virtual void TearDown() { |
935 | + g_clear_object(&sessionmonitor); |
936 | + g_clear_object(&systemmonitor); |
937 | + |
938 | g_clear_object(&proxy); |
939 | - g_clear_object(&service); |
940 | + g_clear_object(&testsystem); |
941 | + g_clear_object(&testsession); |
942 | |
943 | g_object_unref(system); |
944 | + g_object_unref(session); |
945 | |
946 | #if 0 |
947 | /* Accounts Service keeps a bunch of references around so we |
948 | @@ -95,8 +134,78 @@ |
949 | void set_property (const gchar * name, GVariant * value) { |
950 | dbus_test_dbus_mock_object_update_property((DbusTestDbusMock *)service_mock, service_mock.get_sound(), name, value, NULL); |
951 | } |
952 | + |
953 | + testing::AssertionResult expectEventually (std::function<testing::AssertionResult(void)> &testfunc) { |
954 | + auto loop = std::shared_ptr<GMainLoop>(g_main_loop_new(nullptr, FALSE), [](GMainLoop * loop) { if (loop != nullptr) g_main_loop_unref(loop); }); |
955 | + |
956 | + std::promise<testing::AssertionResult> retpromise; |
957 | + auto retfuture = retpromise.get_future(); |
958 | + auto start = std::chrono::steady_clock::now(); |
959 | + |
960 | + /* The core of the idle function as an object so we can use the C++-isms |
961 | + of attaching the variables and make this code reasonably readable */ |
962 | + std::function<void(void)> idlefunc = [&loop, &retpromise, &testfunc, &start, this]() -> void { |
963 | + auto result = testfunc(); |
964 | + |
965 | + if (result == false && _eventuallyTime > (std::chrono::steady_clock::now() - start)) { |
966 | + return; |
967 | + } |
968 | + |
969 | + retpromise.set_value(result); |
970 | + g_main_loop_quit(loop.get()); |
971 | + }; |
972 | + |
973 | + auto idlesrc = g_idle_add([](gpointer data) -> gboolean { |
974 | + auto func = reinterpret_cast<std::function<void(void)> *>(data); |
975 | + (*func)(); |
976 | + return G_SOURCE_CONTINUE; |
977 | + }, &idlefunc); |
978 | + |
979 | + g_main_loop_run(loop.get()); |
980 | + g_source_remove(idlesrc); |
981 | + |
982 | + return retfuture.get(); |
983 | + } |
984 | + |
985 | + /* Eventually Helpers */ |
986 | + #define _EVENTUALLY_HELPER(oper) \ |
987 | + template <typename... Args> testing::AssertionResult expectEventually##oper (Args&& ... args) { \ |
988 | + std::function<testing::AssertionResult(void)> func = [&]() { \ |
989 | + return testing::internal::CmpHelper##oper(std::forward<Args>(args)...); \ |
990 | + }; \ |
991 | + return expectEventually(func); \ |
992 | + } |
993 | + |
994 | + _EVENTUALLY_HELPER(EQ); |
995 | + _EVENTUALLY_HELPER(NE); |
996 | + _EVENTUALLY_HELPER(LT); |
997 | + _EVENTUALLY_HELPER(GT); |
998 | + _EVENTUALLY_HELPER(STREQ); |
999 | + _EVENTUALLY_HELPER(STRNE); |
1000 | + |
1001 | + #undef _EVENTUALLY_HELPER |
1002 | }; |
1003 | |
1004 | +/* Helpers */ |
1005 | +#define EXPECT_EVENTUALLY_EQ(expected, actual) \ |
1006 | + EXPECT_PRED_FORMAT2(MediaPlayerUserTest::expectEventuallyEQ, expected, actual) |
1007 | + |
1008 | +#define EXPECT_EVENTUALLY_NE(expected, actual) \ |
1009 | + EXPECT_PRED_FORMAT2(MediaPlayerUserTest::expectEventuallyNE, expected, actual) |
1010 | + |
1011 | +#define EXPECT_EVENTUALLY_LT(expected, actual) \ |
1012 | + EXPECT_PRED_FORMAT2(MediaPlayerUserTest::expectEventuallyLT, expected, actual) |
1013 | + |
1014 | +#define EXPECT_EVENTUALLY_GT(expected, actual) \ |
1015 | + EXPECT_PRED_FORMAT2(MediaPlayerUserTest::expectEventuallyGT, expected, actual) |
1016 | + |
1017 | +#define EXPECT_EVENTUALLY_STREQ(expected, actual) \ |
1018 | + EXPECT_PRED_FORMAT2(MediaPlayerUserTest::expectEventuallySTREQ, expected, actual) |
1019 | + |
1020 | +#define EXPECT_EVENTUALLY_STRNE(expected, actual) \ |
1021 | + EXPECT_PRED_FORMAT2(MediaPlayerUserTest::expectEventuallySTRNE, expected, actual) |
1022 | + |
1023 | + |
1024 | TEST_F(MediaPlayerUserTest, BasicObject) { |
1025 | MediaPlayerUser * player = media_player_user_new("user"); |
1026 | ASSERT_NE(nullptr, player); |
1027 | @@ -125,7 +234,12 @@ |
1028 | g_clear_object(&player); |
1029 | } |
1030 | |
1031 | -TEST_F(MediaPlayerUserTest, DataSet) { |
1032 | +void |
1033 | +running_update (GObject * obj, GParamSpec * pspec, bool * running) { |
1034 | + *running = media_player_get_is_running(MEDIA_PLAYER(obj)) == TRUE; |
1035 | +}; |
1036 | + |
1037 | +TEST_F(MediaPlayerUserTest, DISABLED_DataSet) { |
1038 | /* Put data into Acts */ |
1039 | set_property("Timestamp", g_variant_new_uint64(g_get_monotonic_time())); |
1040 | set_property("PlayerName", g_variant_new_string("The Player Formerly Known as Prince")); |
1041 | @@ -141,11 +255,11 @@ |
1042 | MediaPlayerUser * player = media_player_user_new("user"); |
1043 | ASSERT_NE(nullptr, player); |
1044 | |
1045 | - /* Get the proxy -- and it's precious precious data -- oh, my, precious! */ |
1046 | - loop(100); |
1047 | - |
1048 | /* Ensure even with the proxy we don't have anything */ |
1049 | - EXPECT_TRUE(media_player_get_is_running(MEDIA_PLAYER(player))); |
1050 | + bool running = false; |
1051 | + g_signal_connect(G_OBJECT(player), "notify::is-running", G_CALLBACK(running_update), &running); |
1052 | + running_update(G_OBJECT(player), nullptr, &running); |
1053 | + EXPECT_EVENTUALLY_EQ(true, running); |
1054 | EXPECT_TRUE(media_player_get_can_raise(MEDIA_PLAYER(player))); |
1055 | EXPECT_STREQ("user", media_player_get_id(MEDIA_PLAYER(player))); |
1056 | EXPECT_STREQ("The Player Formerly Known as Prince", media_player_get_name(MEDIA_PLAYER(player))); |
1057 | @@ -168,7 +282,7 @@ |
1058 | g_clear_object(&player); |
1059 | } |
1060 | |
1061 | -TEST_F(MediaPlayerUserTest, TimeoutTest) { |
1062 | +TEST_F(MediaPlayerUserTest, DISABLED_TimeoutTest) { |
1063 | /* Put data into Acts -- but 15 minutes ago */ |
1064 | set_property("Timestamp", g_variant_new_uint64(g_get_monotonic_time() - 15 * 60 * 1000 * 1000)); |
1065 | set_property("PlayerName", g_variant_new_string("The Player Formerly Known as Prince")); |
1066 | @@ -180,24 +294,26 @@ |
1067 | set_property("Album", g_variant_new_string("Vinyl is dead")); |
1068 | set_property("ArtUrl", g_variant_new_string("http://art.url")); |
1069 | |
1070 | - /* Ensure the properties get set before we pull them */ |
1071 | - loop(100); |
1072 | - |
1073 | /* Build our media player */ |
1074 | MediaPlayerUser * player = media_player_user_new("user"); |
1075 | ASSERT_NE(nullptr, player); |
1076 | |
1077 | - /* Get the proxy -- and the old data, so old, like forever */ |
1078 | - loop(100); |
1079 | + bool running = false; |
1080 | + g_signal_connect(G_OBJECT(player), "notify::is-running", G_CALLBACK(running_update), &running); |
1081 | + running_update(G_OBJECT(player), nullptr, &running); |
1082 | |
1083 | /* Ensure that we show up as not running */ |
1084 | - EXPECT_FALSE(media_player_get_is_running(MEDIA_PLAYER(player))); |
1085 | + EXPECT_EVENTUALLY_EQ(false, running); |
1086 | |
1087 | /* Update to make running */ |
1088 | set_property("Timestamp", g_variant_new_uint64(g_get_monotonic_time())); |
1089 | - loop(100); |
1090 | - |
1091 | - EXPECT_TRUE(media_player_get_is_running(MEDIA_PLAYER(player))); |
1092 | + |
1093 | + EXPECT_EVENTUALLY_EQ(true, running); |
1094 | + |
1095 | + /* Clear to not run */ |
1096 | + set_property("Timestamp", g_variant_new_uint64(1)); |
1097 | + |
1098 | + EXPECT_EVENTUALLY_EQ(false, running); |
1099 | |
1100 | g_clear_object(&in_icon); |
1101 | g_clear_object(&player); |
1102 | |
1103 | === modified file 'tests/notifications-test.cc' |
1104 | --- tests/notifications-test.cc 2015-02-24 19:05:10 +0000 |
1105 | +++ tests/notifications-test.cc 2015-10-13 13:18:26 +0000 |
1106 | @@ -350,29 +350,29 @@ |
1107 | |
1108 | /* Set high volume with volume change */ |
1109 | notifications->clearNotifications(); |
1110 | - volume_control_mock_set_mock_high_volume(VOLUME_CONTROL_MOCK(volumeControl.get()), TRUE); |
1111 | + volume_control_mock_set_high_volume(VOLUME_CONTROL_MOCK(volumeControl.get()), true); |
1112 | setMockVolume(volumeControl, 0.90); |
1113 | loop(50); |
1114 | notev = notifications->getNotifications(); |
1115 | ASSERT_LT(0, notev.size()); /* This passes with one or two since it would just be an update to the first if a second was sent */ |
1116 | EXPECT_EQ("Volume", notev[0].summary); |
1117 | - EXPECT_EQ("High volume", notev[0].body); |
1118 | + EXPECT_EQ("High volume can damage your hearing.", notev[0].body); |
1119 | EXPECT_GVARIANT_EQ("@s 'true'", notev[0].hints["x-canonical-value-bar-tint"]); |
1120 | |
1121 | /* Move it back */ |
1122 | - volume_control_mock_set_mock_high_volume(VOLUME_CONTROL_MOCK(volumeControl.get()), FALSE); |
1123 | + volume_control_mock_set_high_volume(VOLUME_CONTROL_MOCK(volumeControl.get()), false); |
1124 | setMockVolume(volumeControl, 0.50); |
1125 | loop(50); |
1126 | |
1127 | /* Set high volume without level change */ |
1128 | /* NOTE: This can happen if headphones are plugged in */ |
1129 | notifications->clearNotifications(); |
1130 | - volume_control_mock_set_mock_high_volume(VOLUME_CONTROL_MOCK(volumeControl.get()), TRUE); |
1131 | + volume_control_mock_set_high_volume(VOLUME_CONTROL_MOCK(volumeControl.get()), TRUE); |
1132 | loop(50); |
1133 | notev = notifications->getNotifications(); |
1134 | ASSERT_EQ(1, notev.size()); |
1135 | EXPECT_EQ("Volume", notev[0].summary); |
1136 | - EXPECT_EQ("High volume", notev[0].body); |
1137 | + EXPECT_EQ("High volume can damage your hearing.", notev[0].body); |
1138 | EXPECT_GVARIANT_EQ("@s 'true'", notev[0].hints["x-canonical-value-bar-tint"]); |
1139 | } |
1140 | |
1141 | @@ -406,7 +406,7 @@ |
1142 | EXPECT_EQ(1, notev.size()); |
1143 | } |
1144 | |
1145 | -TEST_F(NotificationsTest, ExtendendVolumeNotification) { |
1146 | +TEST_F(NotificationsTest, DISABLED_ExtendendVolumeNotification) { |
1147 | auto volumeControl = volumeControlMock(); |
1148 | auto soundService = standardService(volumeControl, playerListMock()); |
1149 | |
1150 | @@ -424,7 +424,7 @@ |
1151 | |
1152 | /* Allow an amplified volume */ |
1153 | notifications->clearNotifications(); |
1154 | - indicator_sound_service_set_allow_amplified_volume(soundService.get(), TRUE); |
1155 | + //indicator_sound_service_set_allow_amplified_volume(soundService.get(), TRUE); |
1156 | loop(50); |
1157 | notev = notifications->getNotifications(); |
1158 | ASSERT_EQ(1, notev.size()); |
1159 | @@ -440,7 +440,7 @@ |
1160 | |
1161 | /* Put back */ |
1162 | notifications->clearNotifications(); |
1163 | - indicator_sound_service_set_allow_amplified_volume(soundService.get(), FALSE); |
1164 | + //indicator_sound_service_set_allow_amplified_volume(soundService.get(), FALSE); |
1165 | loop(50); |
1166 | notev = notifications->getNotifications(); |
1167 | ASSERT_EQ(1, notev.size()); |
1168 | |
1169 | === modified file 'tests/volume-control-mock.vala' |
1170 | --- tests/volume-control-mock.vala 2015-02-17 16:54:53 +0000 |
1171 | +++ tests/volume-control-mock.vala 2015-10-13 13:18:26 +0000 |
1172 | @@ -20,12 +20,14 @@ |
1173 | |
1174 | public class VolumeControlMock : VolumeControl |
1175 | { |
1176 | + private bool _high_volume = false; |
1177 | + public override bool high_volume { get { return _high_volume; } protected set { _high_volume = value; } } |
1178 | + public void set_high_volume(bool b) { high_volume = b; } |
1179 | + |
1180 | public string mock_stream { get; set; default = "multimedia"; } |
1181 | public override string stream { get { return mock_stream; } } |
1182 | public override bool ready { get; set; } |
1183 | public override bool active_mic { get; set; } |
1184 | - public bool mock_high_volume { get; set; } |
1185 | - public override bool high_volume { get { return mock_high_volume; } } |
1186 | public bool mock_mute { get; set; } |
1187 | public override bool mute { get { return mock_mute; } } |
1188 | public bool mock_is_playing { get; set; } |
1189 | |
1190 | === modified file 'tests/volume-control-test.cc' |
1191 | --- tests/volume-control-test.cc 2015-02-09 20:14:15 +0000 |
1192 | +++ tests/volume-control-test.cc 2015-10-13 13:18:26 +0000 |
1193 | @@ -32,7 +32,11 @@ |
1194 | DbusTestService * service = NULL; |
1195 | GDBusConnection * session = NULL; |
1196 | |
1197 | - virtual void SetUp() { |
1198 | + virtual void SetUp() override { |
1199 | + |
1200 | + g_setenv("GSETTINGS_SCHEMA_DIR", SCHEMA_DIR, TRUE); |
1201 | + g_setenv("GSETTINGS_BACKEND", "memory", TRUE); |
1202 | + |
1203 | service = dbus_test_service_new(NULL); |
1204 | dbus_test_service_start_tasks(service); |
1205 |
PASSED: Continuous integration, rev:501 jenkins. qa.ubuntu. com/job/ indicator- sound-15. 04-ci/1/ jenkins. qa.ubuntu. com/job/ indicator- sound-15. 04-vivid- amd64-ci/ 1 jenkins. qa.ubuntu. com/job/ indicator- sound-15. 04-vivid- armhf-ci/ 1 jenkins. qa.ubuntu. com/job/ indicator- sound-15. 04-vivid- armhf-ci/ 1/artifact/ work/output/ *zip*/output. zip
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild: s-jenkins. ubuntu- ci:8080/ job/indicator- sound-15. 04-ci/1/ rebuild
http://