Merge lp:~ted/indicator-sound/extreme-volume-warning into lp:indicator-sound/14.10

Proposed by Ted Gould
Status: Superseded
Proposed branch: lp:~ted/indicator-sound/extreme-volume-warning
Merge into: lp:indicator-sound/14.10
Prerequisite: lp:~ted/indicator-sound/synchronous-notification
Diff against target: 263 lines (+139/-15)
4 files modified
src/service.vala (+16/-0)
src/sound-menu.vala (+39/-1)
src/volume-control.vala (+66/-14)
tests/manual (+18/-0)
To merge this branch: bzr merge lp:~ted/indicator-sound/extreme-volume-warning
Reviewer Review Type Date Requested Status
Charles Kerr (community) Approve
PS Jenkins bot (community) continuous-integration Approve
Review via email: mp+238168@code.launchpad.net

This proposal has been superseded by a proposal from 2014-10-31.

Commit message

Warn on high audio levels when using headphones

Description of the change

Both a notification change and a menu item for showing that there is high volume.

To post a comment you must log in.
Revision history for this message
Mirco Müller (macslow) wrote :

Ted, I think you can drop this branch. For fixing LP: #1373404 I've the more fitting solution with a patch to lp:unity-settings-daemon, which is the one process triggering volume-notifications upon hardware multi-media-keys already.

My branch lp:~macslow/unity-settings-daemon/max-volume-notification-warning does also fully comply with the dedicated design for the hig-volume-warning.

Revision history for this message
Mirco Müller (macslow) wrote :

Ignore my last comment. I've thought I was clever, but wasn't really :/ I'll take a look at your branch with my new sync.-notification-support in unity8's notification backend/frontend.

Revision history for this message
Ricardo Salveti (rsalveti) wrote :

One easy way to find out if the active port is a headphone/headset:
http://paste.ubuntu.com/8556022/

Revision history for this message
Michał Sawicz (saviq) wrote :

FYI, testing in rtm silo 6 yielded a few issues:
- sound played only once (I know Mirco explains this somewhere)
- I still had the sound when playing music, which got paused
- when the notification disappeared, the first subsequent volume button press resulted in an incorrect level notification displayed
- notification displayed and sound played when changing volume through the indicator menu slider
- no warning text displayed, just the bar went orange (when playing music with headphones plugged in)

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Charles Kerr (charlesk) wrote :

New to this patch, indicator-sound is calling /org/freedesktop/Notifications's Notify() method on startup with an "audio-volume-muted" string. This appears to be a side-effect of constructing indicator-sound, as it shows muted regardless of whether sound is actually muted.

This is consistently reproducible by running "restart indicator-sound" from the shell after installing this patch.

review: Needs Fixing
Revision history for this message
Charles Kerr (charlesk) wrote :

NVM that last, I see ted's already got lars' fix for that queued into 15.04 in https://code.launchpad.net/~ted/indicator-sound/lp1381871-no-initial-value-for-utopic

WTF, in testing the only issue I found was bug #1389008, which interfered with testing but doesn't seem to be new to this patch.

review: Approve

Unmerged revisions

485. By Ted Gould

Make sure to check the item in the loop

484. By Ted Gould

Removing the remove code that was supposed to be removed earlier

483. By Ted Gould

Commenting out the sound for now

482. By Ted Gould

Make volume icons match the panel icons

481. By Ted Gould

Found other notification

480. By Ted Gould

Remove volume text from manual test

479. By Ted Gould

Get propery property name

478. By Ted Gould

Check more precisely the valid roles

477. By Mirco Müller <email address hidden>

Make High-volume text work correctly

476. By Ted Gould

Merge trunk

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/service.vala'
2--- src/service.vala 2014-10-03 14:27:38 +0000
3+++ src/service.vala 2014-10-15 18:52:57 +0000
4@@ -49,6 +49,7 @@
5 this.actions.add_action (this.create_mute_action ());
6 this.actions.add_action (this.create_volume_action ());
7 this.actions.add_action (this.create_mic_volume_action ());
8+ this.actions.add_action (this.create_high_volume_actions ());
9
10 this.menus = new HashTable<string, SoundMenu> (str_hash, str_equal);
11 this.menus.insert ("desktop_greeter", new SoundMenu (null, SoundMenu.DisplayFlags.SHOW_MUTE | SoundMenu.DisplayFlags.HIDE_PLAYERS | SoundMenu.DisplayFlags.GREETER_PLAYERS));
12@@ -60,6 +61,10 @@
13 this.volume_control.bind_property ("active-mic", menu, "show-mic-volume", BindingFlags.SYNC_CREATE);
14 });
15
16+ this.menus.@foreach ( (profile, menu) => {
17+ this.volume_control.bind_property ("high-volume", menu, "show-high-volume-warning", BindingFlags.SYNC_CREATE);
18+ });
19+
20 this.sync_preferred_players ();
21 this.settings.changed["interested-media-players"].connect ( () => {
22 this.sync_preferred_players ();
23@@ -174,6 +179,8 @@
24 double v = this.volume_control.get_volume () + volume_step_percentage * delta;
25 this.volume_control.set_volume (v.clamp (0.0, this.max_volume));
26
27+ /* TODO: Don't want to mess up the desktop today, but we should remove this
28+ scrolling change and merge that into volume control's notification */
29 if (this.notification != null) {
30 string icon;
31 if (v <= 0.0)
32@@ -388,6 +395,15 @@
33 return volume_action;
34 }
35
36+ Action create_high_volume_actions () {
37+ var high_volume_action = new SimpleAction.stateful("high-volume", null, new Variant.boolean (this.volume_control.high_volume));
38+
39+ this.volume_control.notify["high-volume"].connect( () =>
40+ high_volume_action.set_state(new Variant.boolean (this.volume_control.high_volume)));
41+
42+ return high_volume_action;
43+ }
44+
45 void bus_acquired (DBusConnection connection, string name) {
46 try {
47 connection.export_action_group ("/com/canonical/indicator/sound", this.actions);
48
49=== modified file 'src/sound-menu.vala'
50--- src/sound-menu.vala 2014-10-08 01:57:56 +0000
51+++ src/sound-menu.vala 2014-10-15 18:52:57 +0000
52@@ -93,12 +93,49 @@
53 this.mic_volume_shown = true;
54 }
55 else if (!value && this.mic_volume_shown) {
56- this.volume_section.remove (this.volume_section.get_n_items () -1);
57+ int location = -1;
58+ while ((location = find_action(this.volume_section, "indicator.mic-volume")) != -1) {
59+ this.volume_section.remove (location);
60+ }
61 this.mic_volume_shown = false;
62 }
63 }
64 }
65
66+ public bool show_high_volume_warning {
67+ get {
68+ return this.high_volume_warning_shown;
69+ }
70+ set {
71+ if (value && !this.high_volume_warning_shown) {
72+ /* NOTE: Action doesn't really exist, just used to find below when removing */
73+ var item = new MenuItem(_("High volume can damage your hearing."), "indicator.high-volume-warning-item");
74+ volume_section.append_item (item);
75+ this.high_volume_warning_shown = true;
76+ }
77+ else if (!value && this.high_volume_warning_shown) {
78+ int location = -1;
79+ while ((location = find_action(this.volume_section, "indicator.high-volume-warning-item")) != -1) {
80+ this.volume_section.remove (location);
81+ }
82+ this.high_volume_warning_shown = false;
83+ }
84+ }
85+ }
86+
87+ int find_action (Menu menu, string in_action) {
88+ int n = menu.get_n_items ();
89+ for (int i = 0; i < n; i++) {
90+ string action;
91+ menu.get_item_attribute (i, "action", "s", out action);
92+ if (in_action == action)
93+ return i;
94+ }
95+
96+ return -1;
97+ }
98+
99+
100 public void add_player (MediaPlayer player) {
101 if (this.notify_handlers.contains (player))
102 return;
103@@ -141,6 +178,7 @@
104 Menu volume_section;
105 bool mic_volume_shown;
106 bool settings_shown = false;
107+ bool high_volume_warning_shown = false;
108 bool hide_inactive;
109 bool hide_players = false;
110 HashTable<MediaPlayer, ulong> notify_handlers;
111
112=== modified file 'src/volume-control.vala'
113--- src/volume-control.vala 2014-10-15 18:52:57 +0000
114+++ src/volume-control.vala 2014-10-15 18:52:57 +0000
115@@ -70,6 +70,8 @@
116 private double _account_service_volume = 0.0;
117 private Notify.Notification _notification;
118
119+ private bool _active_port_headphone = false;
120+
121 public signal void volume_changed (double v);
122 public signal void mic_volume_changed (double v);
123
124@@ -79,6 +81,9 @@
125 /** true when a microphone is active **/
126 public bool active_mic { get; private set; default = false; }
127
128+ /** true when high volume warnings should be shown */
129+ public bool high_volume { get; set; }
130+
131 public VolumeControl ()
132 {
133 if (loop == null)
134@@ -158,6 +163,8 @@
135
136 private void sink_info_cb_for_props (Context c, SinkInfo? i, int eol)
137 {
138+ bool old_active_port_headphone = this._active_port_headphone;
139+
140 if (i == null)
141 return;
142
143@@ -174,12 +181,27 @@
144 this.notify_property ("is-playing");
145 }
146
147+ /* Check if the current active port is headset/headphone */
148+ /* There is not easy way to check if the port is a headset/headphone besides
149+ * checking for the port name. On touch (with the pulseaudio droid element)
150+ * the headset/headphone port is called 'output-headset' and 'output-headphone'.
151+ * On the desktop this is usually called 'analog-output-headphones' */
152+ if (i.active_port.name == "output-wired_headset" ||
153+ i.active_port.name == "output-wired_headphone" ||
154+ i.active_port.name == "analog-output-headphones") {
155+ _active_port_headphone = true;
156+ } else {
157+ _active_port_headphone = false;
158+ }
159+
160 if (_pulse_use_stream_restore == false &&
161 _volume != volume_to_double (i.volume.max ()))
162 {
163 _volume = volume_to_double (i.volume.max ());
164 volume_changed (_volume);
165 start_local_volume_timer();
166+ } else if (this._active_port_headphone != old_active_port_headphone) {
167+ volume_changed (_volume);
168 }
169 }
170
171@@ -583,22 +605,52 @@
172 {
173 /* Using this to detect whether we're on the phone or not */
174 if (_pulse_use_stream_restore) {
175- if (volume == 0.0)
176- _notification.update (_("Volume"), "", "audio-volume-muted");
177- if (volume > 0.0 && volume <= 0.33)
178- _notification.update (_("Volume"), "", "audio-volume-low");
179- if (volume > 0.33 && volume <= 0.66)
180- _notification.update (_("Volume"), "", "audio-volume-medium");
181- if (volume > 0.66 && volume <= 1.0)
182- _notification.update (_("Volume"), "", "audio-volume-high");
183+ /* Watch for extreme */
184+ if (volume > 0.75 && _active_port_headphone)
185+ high_volume = true;
186+ else
187+ high_volume = false;
188+
189+ /* Determine Label */
190+ string volume_label = "";
191+ if (high_volume)
192+ volume_label = _("High volume");
193+
194+ /* Choose an icon */
195+ string icon = "audio-volume-muted";
196+ if (volume <= 0.0)
197+ icon = "audio-volume-muted";
198+ else if (volume <= 0.3)
199+ icon = "audio-volume-low";
200+ else if (volume <= 0.7)
201+ icon = "audio-volume-medium";
202+ else
203+ icon = "audio-volume-high";
204+
205+ /* Choose a sound */
206+ string? sound = null;
207+ if (!((_active_sink_input >= 0) && (_active_sink_input < _valid_roles.length)
208+ && (_valid_roles[_active_sink_input] == "multimedia")))
209+ sound = "/usr/share/sounds/ubuntu/stereo/message.ogg";
210+
211+ /* Check tint */
212+ string tint = "false";
213+ if (high_volume)
214+ tint = "true";
215+
216+ /* Put it all into the notification */
217+ _notification.clear_hints ();
218+ _notification.update (_("Volume"), volume_label, icon);
219 _notification.set_hint ("value", (int32)(volume * 100.0));
220- if (_active_sink_input == -1 || _valid_roles[_active_sink_input] != "multimedia") {
221- /* No audio ping if we're playing multimedia */
222- _notification.set_hint ("sound-file", "/usr/share/sounds/ubuntu/stereo/message.ogg");
223- } else {
224- _notification.set_hint ("sound-file", null);
225- }
226+ /* TODO: Removing sound until we can get all the roles cleaned up for
227+ when to play it. We expect this to come back, but in another landing.
228+ _notification.set_hint ("sound-file", sound);
229+ */
230+ _notification.set_hint ("x-canonical-value-bar-tint", tint);
231+ _notification.set_hint ("x-canonical-private-synchronous", "true");
232+ _notification.set_hint ("x-canonical-non-shaped-icon", "true");
233
234+ /* Show it */
235 try {
236 _notification.show ();
237 } catch (GLib.Error e) {
238
239=== modified file 'tests/manual'
240--- tests/manual 2014-10-15 18:52:57 +0000
241+++ tests/manual 2014-10-15 18:52:57 +0000
242@@ -39,3 +39,21 @@
243 <dd>A notification bubble should appear with the sound volume</dd>
244 <dd>No notification sound should be heard</dd>
245 </dl>
246+
247+Test-case indicator-sound/unity8-high-volume
248+<dl>
249+ <dt>Plug headphones into the headphone jack</dt>
250+ <dt>Adjust volume so that it is at the midpoint of volume range</dt>
251+ <dd>The slider should be in the middle of the scale</dd>
252+ <dt>Increase the volume once using HW keys if available</dt>
253+ <dd>A notification bubble should appear with the sound volume</dd>
254+ <dd>There should be no text on the notification</dd>
255+ <dt>Increase the volume using HW keys until it is roughly 90% of the range</dt>
256+ <dd>A notification bubble should appear with the sound volume</dd>
257+ <dd>The text on the notification should read "High volume"</dd>
258+ <dd>The range on the notification bubble should have a different color signifying the higher volume</dd>
259+ <dt>Decrease the volume using HW keys until it is roughly 50% of the range</dt>
260+ <dd>A notification bubble should appear with the sound volume</dd>
261+ <dd>There should be no text on the notification</dd>
262+ <dd>The range on the notification bubble should have a standard color</dd>
263+</dl>

Subscribers

People subscribed via source and target branches