diff options
-rw-r--r-- | src/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/media-player-mpris.vala | 3 | ||||
-rw-r--r-- | src/service.vala | 356 | ||||
-rw-r--r-- | src/sound-menu.vala | 38 | ||||
-rw-r--r-- | src/volume-control-pulse.vala | 85 | ||||
-rw-r--r-- | src/volume-control.vala | 14 | ||||
-rw-r--r-- | tests/integration/indicator-sound-test-base.cpp | 181 | ||||
-rw-r--r-- | tests/integration/indicator-sound-test-base.h | 18 | ||||
-rw-r--r-- | tests/integration/test-indicator.cpp | 81 | ||||
-rw-r--r-- | tests/notifications-test.cc | 2 |
10 files changed, 694 insertions, 85 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4a25deb..73a270c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -103,6 +103,7 @@ vala_add(indicator-sound-service sound-menu.vala DEPENDS media-player + volume-control ) vala_add(indicator-sound-service accounts-service-user.vala diff --git a/src/media-player-mpris.vala b/src/media-player-mpris.vala index b9961f5..780d724 100644 --- a/src/media-player-mpris.vala +++ b/src/media-player-mpris.vala @@ -290,7 +290,8 @@ public class MediaPlayerMpris: MediaPlayer { if (changed_properties.lookup ("PlaybackStatus", "s", null)) { this.state = this.proxy.PlaybackStatus != null ? this.proxy.PlaybackStatus : "Unknown"; } - if (changed_properties.lookup ("CanGoNext", "b", null) || changed_properties.lookup ("CanGoPrevious", "b", null)) { + if (changed_properties.lookup ("CanGoNext", "b", null) || changed_properties.lookup ("CanGoPrevious", "b", null) || + changed_properties.lookup ("CanPlay", "b", null) || changed_properties.lookup ("CanPause", "b", null) ) { this.playbackstatus_changed (); } diff --git a/src/service.vala b/src/service.vala index a08edf3..8b08dc0 100644 --- a/src/service.vala +++ b/src/service.vala @@ -52,6 +52,8 @@ public class IndicatorSound.Service: Object { this.notify["visible"].connect ( () => this.update_root_icon () ); this.volume_control = volume; + this.volume_control.active_output_changed.connect (this.update_root_icon); + this.volume_control.active_output_changed.connect (this.update_notification); this.accounts_service = accounts; /* If we're on the greeter, don't export */ @@ -90,6 +92,10 @@ public class IndicatorSound.Service: Object { this.volume_control.bind_property ("high-volume", menu, "show-high-volume-warning", BindingFlags.SYNC_CREATE); }); + this.menus.@foreach ( (profile, menu) => { + this.volume_control.active_output_changed.connect (menu.update_volume_slider); + }); + this.sync_preferred_players (); this.settings.changed["interested-media-players"].connect ( () => { this.sync_preferred_players (); @@ -245,17 +251,7 @@ public class IndicatorSound.Service: Object { void update_root_icon () { double volume = this.volume_control.volume.volume; - string icon; - if (this.volume_control.mute || volume <= 0.0) - icon = this.mute_blocks_sound ? "audio-volume-muted-blocking-panel" : "audio-volume-muted-panel"; - else if (this.accounts_service != null && this.accounts_service.silentMode) - icon = "audio-volume-muted-panel"; - else if (volume <= 0.3) - icon = "audio-volume-low-panel"; - else if (volume <= 0.7) - icon = "audio-volume-medium-panel"; - else - icon = "audio-volume-high-panel"; + string icon = get_volume_root_icon (volume, this.volume_control.mute, volume_control.active_output); string accessible_name; if (this.volume_control.mute) { @@ -282,6 +278,292 @@ public class IndicatorSound.Service: Object { private bool notify_server_supports_sync = false; private bool block_info_notifications = false; + private string get_volume_icon (double volume, VolumeControl.ActiveOutput active_output) + { + string icon = ""; + switch (active_output) + { + case VolumeControl.ActiveOutput.SPEAKERS: + if (volume <= 0.0) + icon = "audio-volume-muted"; + else if (volume <= 0.3) + icon = "audio-volume-low"; + else if (volume <= 0.7) + icon = "audio-volume-medium"; + else + icon = "audio-volume-high"; + break; + case VolumeControl.ActiveOutput.HEADPHONES: + if (volume <= 0.0) + icon = "audio-volume-muted"; + else if (volume <= 0.3) + icon = "audio-volume-low"; + else if (volume <= 0.7) + icon = "audio-volume-medium"; + else + icon = "audio-volume-high"; + break; + case VolumeControl.ActiveOutput.BLUETOOTH_HEADPHONES: + if (volume <= 0.0) + icon = "audio-volume-muted"; + else if (volume <= 0.3) + icon = "audio-volume-low"; + else if (volume <= 0.7) + icon = "audio-volume-medium"; + else + icon = "audio-volume-high"; + break; + case VolumeControl.ActiveOutput.BLUETOOTH_SPEAKER: + if (volume <= 0.0) + icon = "audio-volume-muted"; + else if (volume <= 0.3) + icon = "audio-volume-low"; + else if (volume <= 0.7) + icon = "audio-volume-medium"; + else + icon = "audio-volume-high"; + break; + case VolumeControl.ActiveOutput.USB_SPEAKER: + if (volume <= 0.0) + icon = "audio-volume-muted"; + else if (volume <= 0.3) + icon = "audio-volume-low"; + else if (volume <= 0.7) + icon = "audio-volume-medium"; + else + icon = "audio-volume-high"; + break; + case VolumeControl.ActiveOutput.USB_HEADPHONES: + if (volume <= 0.0) + icon = "audio-volume-muted"; + else if (volume <= 0.3) + icon = "audio-volume-low"; + else if (volume <= 0.7) + icon = "audio-volume-medium"; + else + icon = "audio-volume-high"; + break; + case VolumeControl.ActiveOutput.HDMI_SPEAKER: + if (volume <= 0.0) + icon = "audio-volume-muted"; + else if (volume <= 0.3) + icon = "audio-volume-low"; + else if (volume <= 0.7) + icon = "audio-volume-medium"; + else + icon = "audio-volume-high"; + break; + case VolumeControl.ActiveOutput.HDMI_HEADPHONES: + if (volume <= 0.0) + icon = "audio-volume-muted"; + else if (volume <= 0.3) + icon = "audio-volume-low"; + else if (volume <= 0.7) + icon = "audio-volume-medium"; + else + icon = "audio-volume-high"; + break; + } + return icon; + } + + private string get_volume_root_icon_by_volume (double volume, VolumeControl.ActiveOutput active_output) + { + string icon = ""; + switch (active_output) + { + case VolumeControl.ActiveOutput.SPEAKERS: + if (volume <= 0.0) + icon = "audio-volume-muted-panel"; + else if (volume <= 0.3) + icon = "audio-volume-low-panel"; + else if (volume <= 0.7) + icon = "audio-volume-medium-panel"; + else + icon = "audio-volume-high-panel"; + break; + case VolumeControl.ActiveOutput.HEADPHONES: + if (volume <= 0.0) + icon = "audio-volume-muted-panel"; + else if (volume <= 0.3) + icon = "audio-volume-low-panel"; + else if (volume <= 0.7) + icon = "audio-volume-medium-panel"; + else + icon = "audio-volume-high-panel"; + break; + case VolumeControl.ActiveOutput.BLUETOOTH_HEADPHONES: + if (volume <= 0.0) + icon = "audio-volume-muted-panel"; + else if (volume <= 0.3) + icon = "audio-volume-low-panel"; + else if (volume <= 0.7) + icon = "audio-volume-medium-panel"; + else + icon = "audio-volume-high-panel"; + break; + case VolumeControl.ActiveOutput.BLUETOOTH_SPEAKER: + if (volume <= 0.0) + icon = "audio-volume-muted-panel"; + else if (volume <= 0.3) + icon = "audio-volume-low-panel"; + else if (volume <= 0.7) + icon = "audio-volume-medium-panel"; + else + icon = "audio-volume-high-panel"; + break; + case VolumeControl.ActiveOutput.USB_SPEAKER: + if (volume <= 0.0) + icon = "audio-volume-muted-panel"; + else if (volume <= 0.3) + icon = "audio-volume-low-panel"; + else if (volume <= 0.7) + icon = "audio-volume-medium-panel"; + else + icon = "audio-volume-high-panel"; + break; + case VolumeControl.ActiveOutput.USB_HEADPHONES: + if (volume <= 0.0) + icon = "audio-volume-muted-panel"; + else if (volume <= 0.3) + icon = "audio-volume-low-panel"; + else if (volume <= 0.7) + icon = "audio-volume-medium-panel"; + else + icon = "audio-volume-high-panel"; + break; + case VolumeControl.ActiveOutput.HDMI_SPEAKER: + if (volume <= 0.0) + icon = "audio-volume-muted-panel"; + else if (volume <= 0.3) + icon = "audio-volume-low-panel"; + else if (volume <= 0.7) + icon = "audio-volume-medium-panel"; + else + icon = "audio-volume-high-panel"; + break; + case VolumeControl.ActiveOutput.HDMI_HEADPHONES: + if (volume <= 0.0) + icon = "audio-volume-muted-panel"; + else if (volume <= 0.3) + icon = "audio-volume-low-panel"; + else if (volume <= 0.7) + icon = "audio-volume-medium-panel"; + else + icon = "audio-volume-high-panel"; + break; + } + return icon; + } + + private string get_volume_notification_icon (double volume, bool loud, VolumeControl.ActiveOutput active_output) { + string icon = ""; + if (loud) { + switch (active_output) + { + case VolumeControl.ActiveOutput.SPEAKERS: + icon = "audio-volume-high"; + break; + case VolumeControl.ActiveOutput.HEADPHONES: + icon = "audio-volume-high"; + break; + case VolumeControl.ActiveOutput.BLUETOOTH_HEADPHONES: + icon = "audio-volume-high"; + break; + case VolumeControl.ActiveOutput.BLUETOOTH_SPEAKER: + icon = "audio-volume-high"; + break; + case VolumeControl.ActiveOutput.USB_SPEAKER: + icon = "audio-volume-high"; + break; + case VolumeControl.ActiveOutput.USB_HEADPHONES: + icon = "audio-volume-high"; + break; + case VolumeControl.ActiveOutput.HDMI_SPEAKER: + icon = "audio-volume-high"; + break; + case VolumeControl.ActiveOutput.HDMI_HEADPHONES: + icon = "audio-volume-high"; + break; + } + } else { + icon = get_volume_icon (volume, active_output); + } + return icon; + } + + private string get_volume_root_icon (double volume, bool mute, VolumeControl.ActiveOutput active_output) { + string icon = ""; + switch (active_output) + { + case VolumeControl.ActiveOutput.SPEAKERS: + if (mute || volume <= 0.0) + icon = this.mute_blocks_sound ? "audio-volume-muted-blocking-panel" : "audio-volume-muted-panel"; + else if (this.accounts_service != null && this.accounts_service.silentMode) + icon = "audio-volume-muted-panel"; + else + icon = get_volume_root_icon_by_volume (volume, active_output); + break; + case VolumeControl.ActiveOutput.HEADPHONES: + if (mute || volume <= 0.0) + icon = this.mute_blocks_sound ? "audio-volume-muted-blocking-panel" : "audio-volume-muted-panel"; + else if (this.accounts_service != null && this.accounts_service.silentMode) + icon = "audio-volume-muted-panel"; + else + icon = get_volume_root_icon_by_volume (volume, active_output); + break; + case VolumeControl.ActiveOutput.BLUETOOTH_HEADPHONES: + if (mute || volume <= 0.0) + icon = this.mute_blocks_sound ? "audio-volume-muted-blocking-panel" : "audio-volume-muted-panel"; + else if (this.accounts_service != null && this.accounts_service.silentMode) + icon = "audio-volume-muted-panel"; + else + icon = get_volume_root_icon_by_volume (volume, active_output); + break; + case VolumeControl.ActiveOutput.BLUETOOTH_SPEAKER: + if (mute || volume <= 0.0) + icon = this.mute_blocks_sound ? "audio-volume-muted-blocking-panel" : "audio-volume-muted-panel"; + else if (this.accounts_service != null && this.accounts_service.silentMode) + icon = "audio-volume-muted-panel"; + else + icon = get_volume_root_icon_by_volume (volume, active_output); + break; + case VolumeControl.ActiveOutput.USB_SPEAKER: + if (mute || volume <= 0.0) + icon = this.mute_blocks_sound ? "audio-volume-muted-blocking-panel" : "audio-volume-muted-panel"; + else if (this.accounts_service != null && this.accounts_service.silentMode) + icon = "audio-volume-muted-panel"; + else + icon = get_volume_root_icon_by_volume (volume, active_output); + break; + case VolumeControl.ActiveOutput.USB_HEADPHONES: + if (mute || volume <= 0.0) + icon = this.mute_blocks_sound ? "audio-volume-muted-blocking-panel" : "audio-volume-muted-panel"; + else if (this.accounts_service != null && this.accounts_service.silentMode) + icon = "audio-volume-muted-panel"; + else + icon = get_volume_root_icon_by_volume (volume, active_output); + break; + case VolumeControl.ActiveOutput.HDMI_SPEAKER: + if (mute || volume <= 0.0) + icon = this.mute_blocks_sound ? "audio-volume-muted-blocking-panel" : "audio-volume-muted-panel"; + else if (this.accounts_service != null && this.accounts_service.silentMode) + icon = "audio-volume-muted-panel"; + else + icon = get_volume_root_icon_by_volume (volume, active_output); + break; + case VolumeControl.ActiveOutput.HDMI_HEADPHONES: + if (mute || volume <= 0.0) + icon = this.mute_blocks_sound ? "audio-volume-muted-blocking-panel" : "audio-volume-muted-panel"; + else if (this.accounts_service != null && this.accounts_service.silentMode) + icon = "audio-volume-muted-panel"; + else + icon = get_volume_root_icon_by_volume (volume, active_output); + break; + } + return icon; + } + private void update_notification () { if (!notify_server_caps_checked) { @@ -323,26 +605,46 @@ public class IndicatorSound.Service: Object { if (notify_server_supports_sync && !block_info_notifications) { /* Determine Label */ - unowned string volume_label = loud + string volume_label = loud ? _("High volume can damage your hearing.") : ""; + + if (volume_label == "") { + if (volume_control.active_output == VolumeControl.ActiveOutput.SPEAKERS) { + volume_label = _("Speakers"); + } + + if (volume_control.active_output == VolumeControl.ActiveOutput.HEADPHONES) { + volume_label = _("Headphones"); + } + + if (volume_control.active_output == VolumeControl.ActiveOutput.BLUETOOTH_HEADPHONES) { + volume_label = _("Bluetooth headphones"); + } + + if (volume_control.active_output == VolumeControl.ActiveOutput.BLUETOOTH_SPEAKER) { + volume_label = _("Bluetooth speaker"); + } + + if (volume_control.active_output == VolumeControl.ActiveOutput.USB_SPEAKER) { + volume_label = _("Usb speaker"); + } + + if (volume_control.active_output == VolumeControl.ActiveOutput.USB_HEADPHONES) { + volume_label = _("Usb headphones"); + } + + if (volume_control.active_output == VolumeControl.ActiveOutput.HDMI_SPEAKER) { + volume_label = _("HDMI speaker"); + } + + if (volume_control.active_output == VolumeControl.ActiveOutput.HDMI_HEADPHONES) { + volume_label = _("HDMI headphones"); + } + } /* Choose an icon */ - unowned string icon; - if (loud) { - icon = "audio-volume-high"; - } else { - var volume = volume_control.volume.volume; - - if (volume <= 0.0) - icon = "audio-volume-muted"; - else if (volume <= 0.3) - icon = "audio-volume-low"; - else if (volume <= 0.7) - icon = "audio-volume-medium"; - else - icon = "audio-volume-high"; - } + string icon = get_volume_notification_icon (volume_control.volume.volume, loud, volume_control.active_output); /* Reset the notification */ var n = this.info_notification; diff --git a/src/sound-menu.vala b/src/sound-menu.vala index 7a6044b..b612264 100644 --- a/src/sound-menu.vala +++ b/src/sound-menu.vala @@ -71,6 +71,7 @@ public class SoundMenu: Object this.notify_handlers = new HashTable<MediaPlayer, ulong> (direct_hash, direct_equal); this.greeter_players = (flags & DisplayFlags.GREETER_PLAYERS) != 0; + } ~SoundMenu () { @@ -193,6 +194,43 @@ public class SoundMenu: Object this.notify_handlers.remove (player); } + public void update_volume_slider (VolumeControl.ActiveOutput active_output) { + int index = find_action (this.volume_section, "indicator.volume"); + if (index != -1) { + string label = "Volume"; + switch (active_output) { + case VolumeControl.ActiveOutput.SPEAKERS: + label = "Volume"; + break; + case VolumeControl.ActiveOutput.HEADPHONES: + label = "Volume (Headphones)"; + break; + case VolumeControl.ActiveOutput.BLUETOOTH_SPEAKER: + label = "Volume (Bluetooth)"; + break; + case VolumeControl.ActiveOutput.USB_SPEAKER: + label = "Volume (Usb)"; + break; + case VolumeControl.ActiveOutput.HDMI_SPEAKER: + label = "Volume (HDMI)"; + break; + case VolumeControl.ActiveOutput.BLUETOOTH_HEADPHONES: + label = "Volume (Bluetooth headphones)"; + break; + case VolumeControl.ActiveOutput.USB_HEADPHONES: + label = "Volume (Usb headphones)"; + break; + case VolumeControl.ActiveOutput.HDMI_HEADPHONES: + label = "Volume (HDMI headphones)"; + break; + } + this.volume_section.remove (index); + this.volume_section.insert_item (index, this.create_slider_menu_item (_(label), "indicator.volume(0)", 0.0, 1.0, 0.01, + "audio-volume-low-zero-panel", + "audio-volume-high-panel")); + } + } + public Menu root; public Menu menu; Menu volume_section; diff --git a/src/volume-control-pulse.vala b/src/volume-control-pulse.vala index ab0d6d7..8122f26 100644 --- a/src/volume-control-pulse.vala +++ b/src/volume-control-pulse.vala @@ -87,6 +87,7 @@ public class VolumeControlPulse : VolumeControl private bool _send_next_local_volume = false; private double _account_service_volume = 0.0; private bool _active_port_headphone = false; + private VolumeControl.ActiveOutput _active_output = VolumeControl.ActiveOutput.SPEAKERS; /** true when connected to the pulse server */ public override bool ready { get; private set; } @@ -135,6 +136,49 @@ public class VolumeControlPulse : VolumeControl stop_high_volume_approved_timer(); } + private VolumeControl.ActiveOutput calculate_active_output (SinkInfo? sink) { + + VolumeControl.ActiveOutput ret_output = VolumeControl.ActiveOutput.SPEAKERS; + /* Check if the current active port is headset/headphone */ + /* There is not easy way to check if the port is a headset/headphone besides + * checking for the port name. On touch (with the pulseaudio droid element) + * the headset/headphone port is called 'output-headset' and 'output-headphone'. + * On the desktop this is usually called 'analog-output-headphones' */ + // look if it's a headset/headphones + if (sink.name == "indicator_sound_test_headphones" || + (sink.active_port != null && + (sink.active_port.name.contains("headset") || + sink.active_port.name.contains("headphone")))) { + _active_port_headphone = true; + // check if it's a bluetooth device + var device_bus = sink.proplist.gets ("device.bus"); + if (device_bus != null && device_bus == "bluetooth") { + ret_output = VolumeControl.ActiveOutput.BLUETOOTH_HEADPHONES; + } else if (device_bus != null && device_bus == "usb") { + ret_output = VolumeControl.ActiveOutput.USB_HEADPHONES; + } else if (device_bus != null && device_bus == "hdmi") { + ret_output = VolumeControl.ActiveOutput.HDMI_HEADPHONES; + } else { + ret_output = VolumeControl.ActiveOutput.HEADPHONES; + } + } else { + // speaker + _active_port_headphone = false; + var device_bus = sink.proplist.gets ("device.bus"); + if (device_bus != null && device_bus == "bluetooth") { + ret_output = VolumeControl.ActiveOutput.BLUETOOTH_SPEAKER; + } else if (device_bus != null && device_bus == "usb") { + ret_output = VolumeControl.ActiveOutput.USB_SPEAKER; + } else if (device_bus != null && device_bus == "hdmi") { + ret_output = VolumeControl.ActiveOutput.HDMI_SPEAKER; + } else { + ret_output = VolumeControl.ActiveOutput.SPEAKERS; + } + } + + return ret_output; + } + /* PulseAudio logic*/ private void context_events_cb (Context c, Context.SubscriptionEventType t, uint32 index) { @@ -201,19 +245,20 @@ public class VolumeControlPulse : VolumeControl this.notify_property ("is-playing"); } - /* Check if the current active port is headset/headphone */ - /* There is not easy way to check if the port is a headset/headphone besides - * checking for the port name. On touch (with the pulseaudio droid element) - * the headset/headphone port is called 'output-headset' and 'output-headphone'. - * On the desktop this is usually called 'analog-output-headphones' */ - if ( (i.active_port != null && - (i.active_port.name == "output-wired_headset" || - i.active_port.name == "output-wired_headphone" || - i.active_port.name == "analog-output-headphones")) || - (i.name == "indicator_sound_test_headphones")) { - _active_port_headphone = true; - } else { - _active_port_headphone = false; + // store the current status of the active output + VolumeControl.ActiveOutput active_output_before = active_output; + + // calculate the output + _active_output = calculate_active_output (i); + + // check if the output has changed, if so... emit a signal + VolumeControl.ActiveOutput active_output_now = active_output; + if (active_output_now != active_output_before) { + this.active_output_changed (active_output_now); + if (active_output_now == VolumeControl.ActiveOutput.SPEAKERS) { + _high_volume_approved = false; + } + update_high_volume(); } if (_pulse_use_stream_restore == false && @@ -276,7 +321,7 @@ public class VolumeControlPulse : VolumeControl } /* We only care about signals if our internal count is zero */ - //if (sig_count == 0) { + if (sig_count == 0) { /* Extract volume and make sure it's not a side effect of us setting it */ Variant body = message.get_body (); Variant varray = body.get_child_value (0); @@ -294,7 +339,7 @@ public class VolumeControlPulse : VolumeControl vol.reason = VolumeControl.VolumeReasons.PULSE_CHANGE; this.volume = vol; } - //} + } } } @@ -537,6 +582,14 @@ public class VolumeControlPulse : VolumeControl } } + public override VolumeControl.ActiveOutput active_output + { + get + { + return _active_output; + } + } + /* Volume operations */ private static PulseAudio.Volume double_to_volume (double vol) { @@ -629,7 +682,7 @@ public class VolumeControlPulse : VolumeControl } set { var volume_changed = (value.volume != _volume.volume); - warning("Setting volume to %f for profile %d because %d", value.volume, _active_sink_input, value.reason); + debug("Setting volume to %f for profile %d because %d", value.volume, _active_sink_input, value.reason); _volume = value; diff --git a/src/volume-control.vala b/src/volume-control.vala index 6efac35..8e615ea 100644 --- a/src/volume-control.vala +++ b/src/volume-control.vala @@ -28,6 +28,17 @@ public abstract class VolumeControl : Object VOLUME_STREAM_CHANGE } + public enum ActiveOutput { + SPEAKERS, + HEADPHONES, + BLUETOOTH_HEADPHONES, + BLUETOOTH_SPEAKER, + USB_SPEAKER, + USB_HEADPHONES, + HDMI_SPEAKER, + HDMI_HEADPHONES + } + public class Volume : Object { public double volume; public VolumeReasons reason; @@ -39,6 +50,7 @@ public abstract class VolumeControl : Object public virtual bool high_volume { get { return false; } protected set { } } public virtual bool mute { get { return false; } } public virtual bool is_playing { get { return false; } } + public virtual VolumeControl.ActiveOutput active_output { get { return VolumeControl.ActiveOutput.SPEAKERS; } } private Volume _volume; public virtual Volume volume { get { return _volume; } set { } } public virtual double mic_volume { get { return 0.0; } set { } } @@ -56,4 +68,6 @@ public abstract class VolumeControl : Object v.reason = reason; this.volume = v; } + + public signal void active_output_changed (VolumeControl.ActiveOutput active_output); } diff --git a/tests/integration/indicator-sound-test-base.cpp b/tests/integration/indicator-sound-test-base.cpp index f9db365..c261fcf 100644 --- a/tests/integration/indicator-sound-test-base.cpp +++ b/tests/integration/indicator-sound-test-base.cpp @@ -209,7 +209,7 @@ void IndicatorSoundTestBase::stopTestSound() testSoundProcess.terminate(); } -void IndicatorSoundTestBase::startPulseDesktop() +void IndicatorSoundTestBase::startPulseDesktop(DevicePortType speakerPort, DevicePortType headphonesPort) { try { @@ -225,8 +225,8 @@ void IndicatorSoundTestBase::startPulseDesktop() << "--system=false" << "--exit-idle-time=-1" << "-n" - << QString("--load=module-null-sink sink_name=indicator_sound_test_speaker") - << QString("--load=module-null-sink sink_name=indicator_sound_test_headphones") + << QString("--load=module-null-sink sink_name=indicator_sound_test_speaker sink_properties=device.bus=%1").arg(getDevicePortString(speakerPort)) + << QString("--load=module-null-sink sink_name=indicator_sound_test_headphones sink_properties=device.bus=%1").arg(getDevicePortString(headphonesPort)) << "--log-target=file:/tmp/pulse-daemon.log" << "--load=module-dbus-protocol" << "--load=module-native-protocol-tcp auth-ip-acl=127.0.0.1" @@ -240,7 +240,7 @@ void IndicatorSoundTestBase::startPulseDesktop() } } -void IndicatorSoundTestBase::startPulsePhone() +void IndicatorSoundTestBase::startPulsePhone(DevicePortType speakerPort, DevicePortType headphonesPort) { try { @@ -256,8 +256,8 @@ void IndicatorSoundTestBase::startPulsePhone() << "--system=false" << "--exit-idle-time=-1" << "-n" - << QString("--load=module-null-sink sink_name=indicator_sound_test_speaker") - << QString("--load=module-null-sink sink_name=indicator_sound_test_headphones") + << QString("--load=module-null-sink sink_name=indicator_sound_test_speaker sink_properties=device.bus=%1").arg(getDevicePortString(speakerPort)) + << QString("--load=module-null-sink sink_name=indicator_sound_test_headphones sink_properties=device.bus=%1").arg(getDevicePortString(headphonesPort)) << "--log-target=file:/tmp/pulse-daemon.log" << QString("--load=module-stream-restore restore_device=false restore_muted=false fallback_table=%1").arg(STREAM_RESTORE_TABLE) << "--load=module-dbus-protocol" @@ -327,10 +327,10 @@ mh::MenuMatcher::Parameters IndicatorSoundTestBase::phoneParameters() "/com/canonical/indicator/sound/phone"); } -unity::gmenuharness::MenuItemMatcher IndicatorSoundTestBase::volumeSlider(double volume) +unity::gmenuharness::MenuItemMatcher IndicatorSoundTestBase::volumeSlider(double volume, QString const &label) { return mh::MenuItemMatcher().radio() - .label("Volume") + .label(label.toStdString()) .round_doubles(0.1) .int32_attribute("target", 0) .double_attribute("min-value", 0.0) @@ -626,3 +626,168 @@ bool IndicatorSoundTestBase::activateHeadphones(bool headphonesActive) return pacltProcess.exitCode() == 0; } + +QString IndicatorSoundTestBase::getDevicePortString(DevicePortType port) +{ + QString portString; + + switch (port) + { + case WIRED: + portString = "wired"; + break; + case BLUETOOTH: + portString = "bluetooth"; + break; + case USB: + portString = "usb"; + break; + case HDMI: + portString = "hdmi"; + break; + default: + portString = "not_defined"; + break; + } + + return portString; +} + +void IndicatorSoundTestBase::checkPortDevicesLabels(DevicePortType speakerPort, DevicePortType headphonesPort) +{ + double INITIAL_VOLUME = 1.0; + + QString speakerString; + QString speakerStringMenu; + switch(speakerPort) + { + case WIRED: + speakerString = "Speakers"; + speakerStringMenu = "Volume"; + break; + case BLUETOOTH: + speakerString = "Bluetooth speaker"; + speakerStringMenu = "Volume (Bluetooth)"; + break; + case USB: + speakerString = "Usb speaker"; + speakerStringMenu = "Volume (Usb)"; + break; + case HDMI: + speakerString = "HDMI speaker"; + speakerStringMenu = "Volume (HDMI)"; + break; + } + + QString headphonesString; + QString headphonesStringMenu; + switch(headphonesPort) + { + case WIRED: + headphonesString = "Headphones"; + headphonesStringMenu = "Volume (Headphones)"; + break; + case BLUETOOTH: + headphonesString = "Bluetooth headphones"; + headphonesStringMenu = "Volume (Bluetooth headphones)"; + break; + case USB: + headphonesString = "Usb headphones"; + headphonesStringMenu = "Volume (Usb headphones)"; + break; + case HDMI: + headphonesString = "HDMI headphones"; + headphonesStringMenu = "Volume (HDMI headphones)"; + break; + } + + QSignalSpy notificationsSpy(¬ificationsMockInterface(), + SIGNAL(MethodCalled(const QString &, const QVariantList &))); + + ASSERT_NO_THROW(startAccountsService()); + ASSERT_NO_THROW(startPulsePhone(speakerPort, headphonesPort)); + + // initialize volumes in pulseaudio + EXPECT_TRUE(setStreamRestoreVolume("alert", INITIAL_VOLUME)); + EXPECT_TRUE(setStreamRestoreVolume("multimedia", INITIAL_VOLUME)); + + // start now the indicator, so it picks the new volumes + ASSERT_NO_THROW(startIndicator()); + + // if the speaker is the normal one it does not emit any notification, as that's + // the default one. + // for the rest it notifies the output + if (speakerPort != WIRED) + { + WAIT_FOR_SIGNALS(notificationsSpy, 3); + + // the first time we also have the calls to + // GetServerInformation and GetCapabilities + checkNotificationWithNoArgs("GetServerInformation", notificationsSpy.at(0)); + checkNotificationWithNoArgs("GetCapabilities", notificationsSpy.at(1)); + checkVolumeNotification(1.0, speakerString, false, notificationsSpy.at(2)); + notificationsSpy.clear(); + } + + // activate the headphones + EXPECT_TRUE(activateHeadphones(true)); + + if (speakerPort == WIRED) + { + WAIT_FOR_SIGNALS(notificationsSpy, 3); + + // the first time we also have the calls to + // GetServerInformation and GetCapabilities + checkNotificationWithNoArgs("GetServerInformation", notificationsSpy.at(0)); + checkNotificationWithNoArgs("GetCapabilities", notificationsSpy.at(1)); + checkVolumeNotification(1.0, headphonesString, false, notificationsSpy.at(2)); + notificationsSpy.clear(); + } + else + { + WAIT_FOR_SIGNALS(notificationsSpy, 1); + checkVolumeNotification(1.0, headphonesString, false, notificationsSpy.at(0)); + notificationsSpy.clear(); + } + + // check the label in the menu + EXPECT_MATCHRESULT(mh::MenuMatcher(phoneParameters()) + .item(mh::MenuItemMatcher() + .action("indicator.root") + .string_attribute("x-canonical-type", "com.canonical.indicator.root") + .string_attribute("x-canonical-scroll-action", "indicator.scroll") + .string_attribute("x-canonical-secondary-action", "indicator.mute") + .string_attribute("submenu-action", "indicator.indicator-shown") + .mode(mh::MenuItemMatcher::Mode::starts_with) + .submenu() + .item(mh::MenuItemMatcher() + .section() + .item(silentModeSwitch(false)) + .item(volumeSlider(1.0, headphonesStringMenu)) + ) + ).match()); + + // deactivate the headphones + EXPECT_TRUE(activateHeadphones(false)); + + WAIT_FOR_SIGNALS(notificationsSpy, 1); + checkVolumeNotification(1.0, speakerString, false, notificationsSpy.at(0)); + notificationsSpy.clear(); + + // check the label in the menu + EXPECT_MATCHRESULT(mh::MenuMatcher(phoneParameters()) + .item(mh::MenuItemMatcher() + .action("indicator.root") + .string_attribute("x-canonical-type", "com.canonical.indicator.root") + .string_attribute("x-canonical-scroll-action", "indicator.scroll") + .string_attribute("x-canonical-secondary-action", "indicator.mute") + .string_attribute("submenu-action", "indicator.indicator-shown") + .mode(mh::MenuItemMatcher::Mode::starts_with) + .submenu() + .item(mh::MenuItemMatcher() + .section() + .item(silentModeSwitch(false)) + .item(volumeSlider(1.0, speakerStringMenu)) + ) + ).match()); +} diff --git a/tests/integration/indicator-sound-test-base.h b/tests/integration/indicator-sound-test-base.h index 20e44fc..6879bcb 100644 --- a/tests/integration/indicator-sound-test-base.h +++ b/tests/integration/indicator-sound-test-base.h @@ -49,13 +49,21 @@ public: ~IndicatorSoundTestBase(); + enum DevicePortType + { + WIRED, + BLUETOOTH, + USB, + HDMI + }; + protected: void SetUp() override; void TearDown() override; void startIndicator(); - void startPulseDesktop(); - void startPulsePhone(); + void startPulseDesktop(DevicePortType speakerPort=WIRED, DevicePortType headphonesPort=WIRED); + void startPulsePhone(DevicePortType speakerPort=WIRED, DevicePortType headphonesPort=WIRED); void startAccountsService(); bool clearGSettingsPlayers(); @@ -78,7 +86,7 @@ protected: static unity::gmenuharness::MenuMatcher::Parameters phoneParameters(); - static unity::gmenuharness::MenuItemMatcher volumeSlider(double volume); + static unity::gmenuharness::MenuItemMatcher volumeSlider(double volume, QString const &label); static unity::gmenuharness::MenuItemMatcher silentModeSwitch(bool toggled); @@ -110,6 +118,10 @@ protected: bool activateHeadphones(bool headphonesActive); + QString getDevicePortString(DevicePortType port); + + void checkPortDevicesLabels(DevicePortType speakerPort, DevicePortType headphonesPort); + QtDBusTest::DBusTestRunner dbusTestRunner; QtDBusMock::DBusMock dbusMock; diff --git a/tests/integration/test-indicator.cpp b/tests/integration/test-indicator.cpp index 1607367..fccaf84 100644 --- a/tests/integration/test-indicator.cpp +++ b/tests/integration/test-indicator.cpp @@ -41,6 +41,7 @@ TEST_F(TestIndicator, PhoneChangeRoleVolume) // initialize volumes in pulseaudio EXPECT_TRUE(setStreamRestoreVolume("alert", INITIAL_VOLUME)); + EXPECT_TRUE(setStreamRestoreVolume("multimedia", INITIAL_VOLUME)); // start now the indicator, so it picks the new volumes ASSERT_NO_THROW(startIndicator()); @@ -79,7 +80,7 @@ TEST_F(TestIndicator, PhoneChangeRoleVolume) .item(mh::MenuItemMatcher() .section() .item(silentModeSwitch(false)) - .item(volumeSlider(randomVolume)) + .item(volumeSlider(randomVolume, "Volume")) ) ).match()); @@ -119,7 +120,7 @@ TEST_F(TestIndicator, PhoneChangeRoleVolume) .item(mh::MenuItemMatcher() .section() .item(silentModeSwitch(false)) - .item(volumeSlider(1.0)) + .item(volumeSlider(1.0, "Volume")) ) ).match()); @@ -164,7 +165,7 @@ TEST_F(TestIndicator, PhoneBasicInitialVolume) .item(mh::MenuItemMatcher() .section() .item(silentModeSwitch(false)) - .item(volumeSlider(INITIAL_VOLUME)) + .item(volumeSlider(INITIAL_VOLUME, "Volume")) ) .item(mh::MenuItemMatcher() .label("Sound Settings…") @@ -199,7 +200,7 @@ TEST_F(TestIndicator, PhoneAddMprisPlayer) .item(mh::MenuItemMatcher() .section() .item(silentModeSwitch(false)) - .item(volumeSlider(INITIAL_VOLUME)) + .item(volumeSlider(INITIAL_VOLUME, "Volume")) ) .item(mh::MenuItemMatcher() .label("Sound Settings…") @@ -229,7 +230,7 @@ TEST_F(TestIndicator, PhoneAddMprisPlayer) .item(mh::MenuItemMatcher() .section() .item(silentModeSwitch(false)) - .item(volumeSlider(INITIAL_VOLUME)) + .item(volumeSlider(INITIAL_VOLUME, "Volume")) ) .item(mh::MenuItemMatcher() .section() @@ -283,7 +284,7 @@ TEST_F(TestIndicator, DesktopBasicInitialVolume) .item(mh::MenuItemMatcher().checkbox() .label("Mute") ) - .item(volumeSlider(INITIAL_VOLUME)) + .item(volumeSlider(INITIAL_VOLUME, "Volume")) ) .item(mh::MenuItemMatcher() .section() @@ -337,7 +338,7 @@ TEST_F(TestIndicator, DesktopAddMprisPlayer) .item(mh::MenuItemMatcher().checkbox() .label("Mute") ) - .item(volumeSlider(INITIAL_VOLUME)) + .item(volumeSlider(INITIAL_VOLUME, "Volume")) ) .item(mh::MenuItemMatcher() .section() @@ -391,7 +392,7 @@ TEST_F(TestIndicator, DesktopMprisPlayerButtonsState) .item(mh::MenuItemMatcher().checkbox() .label("Mute") ) - .item(volumeSlider(INITIAL_VOLUME)) + .item(volumeSlider(INITIAL_VOLUME, "Volume")) ) .item(mh::MenuItemMatcher() .section() @@ -429,7 +430,7 @@ TEST_F(TestIndicator, DesktopMprisPlayerButtonsState) .item(mh::MenuItemMatcher().checkbox() .label("Mute") ) - .item(volumeSlider(INITIAL_VOLUME)) + .item(volumeSlider(INITIAL_VOLUME, "Volume")) ) .item(mh::MenuItemMatcher() .section() @@ -468,7 +469,7 @@ TEST_F(TestIndicator, DesktopMprisPlayerButtonsState) .item(mh::MenuItemMatcher().checkbox() .label("Mute") ) - .item(volumeSlider(INITIAL_VOLUME)) + .item(volumeSlider(INITIAL_VOLUME, "Volume")) ) .item(mh::MenuItemMatcher() .section() @@ -506,7 +507,7 @@ TEST_F(TestIndicator, DesktopMprisPlayerButtonsState) .item(mh::MenuItemMatcher().checkbox() .label("Mute") ) - .item(volumeSlider(INITIAL_VOLUME)) + .item(volumeSlider(INITIAL_VOLUME, "Volume")) ) .item(mh::MenuItemMatcher() .section() @@ -577,7 +578,7 @@ TEST_F(TestIndicator, DesktopChangeRoleVolume) .item(mh::MenuItemMatcher().checkbox() .label("Mute") ) - .item(volumeSlider(randomVolume)) + .item(volumeSlider(randomVolume, "Volume")) ) ).match()); @@ -615,7 +616,7 @@ TEST_F(TestIndicator, DesktopChangeRoleVolume) .item(mh::MenuItemMatcher() .section() .item(silentModeSwitch(false)) - .item(volumeSlider(randomVolume)) + .item(volumeSlider(randomVolume, "Volume")) ) ).match()); @@ -664,7 +665,7 @@ TEST_F(TestIndicator, PhoneNotificationVolume) .item(mh::MenuItemMatcher() .section() .item(silentModeSwitch(false)) - .item(volumeSlider(INITIAL_VOLUME)) + .item(volumeSlider(INITIAL_VOLUME, "Volume")) ) .item(mh::MenuItemMatcher() .label("Sound Settings…") @@ -681,21 +682,21 @@ TEST_F(TestIndicator, PhoneNotificationVolume) // GetServerInformation and GetCapabilities checkNotificationWithNoArgs("GetServerInformation", notificationsSpy.at(0)); checkNotificationWithNoArgs("GetCapabilities", notificationsSpy.at(1)); - checkVolumeNotification(1.0, "", false, notificationsSpy.at(2)); + checkVolumeNotification(1.0, "Speakers", false, notificationsSpy.at(2)); notificationsSpy.clear(); setActionValue("volume", QVariant::fromValue(0.0)); WAIT_FOR_SIGNALS(notificationsSpy, 1) - checkVolumeNotification(0.0, "", false, notificationsSpy.at(0)); + checkVolumeNotification(0.0, "Speakers", false, notificationsSpy.at(0)); notificationsSpy.clear(); setActionValue("volume", QVariant::fromValue(0.5)); WAIT_FOR_SIGNALS(notificationsSpy, 1) - checkVolumeNotification(0.5, "", false, notificationsSpy.at(0)); + checkVolumeNotification(0.5, "Speakers", false, notificationsSpy.at(0)); } TEST_F(TestIndicator, PhoneNotificationWarningVolume) @@ -729,13 +730,14 @@ TEST_F(TestIndicator, PhoneNotificationWarningVolume) // change volume to 0.0... no warning should be emitted setActionValue("volume", QVariant::fromValue(0.0)); - WAIT_FOR_SIGNALS(notificationsSpy, 3); + WAIT_FOR_SIGNALS(notificationsSpy, 4); // the first time we also have the calls to // GetServerInformation and GetCapabilities checkNotificationWithNoArgs("GetServerInformation", notificationsSpy.at(0)); checkNotificationWithNoArgs("GetCapabilities", notificationsSpy.at(1)); - checkVolumeNotification(0.0, "", false, notificationsSpy.at(2)); + checkVolumeNotification(0.0, "Headphones", false, notificationsSpy.at(2)); + checkVolumeNotification(0.0, "Headphones", false, notificationsSpy.at(3)); notificationsSpy.clear(); // change volume to 0.5... no warning should be emitted @@ -743,7 +745,7 @@ TEST_F(TestIndicator, PhoneNotificationWarningVolume) WAIT_FOR_SIGNALS(notificationsSpy, 1); - checkVolumeNotification(0.5, "", false, notificationsSpy.at(0)); + checkVolumeNotification(0.5, "Headphones", false, notificationsSpy.at(0)); notificationsSpy.clear(); // change volume to 1.0... warning should be emitted @@ -779,7 +781,7 @@ TEST_F(TestIndicator, PhoneNotificationWarningVolume) .item(mh::MenuItemMatcher() .section() .item(silentModeSwitch(false)) - .item(volumeSlider(0.74)) + .item(volumeSlider(0.74, "Volume (Headphones)")) ) ).match()); @@ -817,7 +819,7 @@ TEST_F(TestIndicator, PhoneNotificationWarningVolume) .item(mh::MenuItemMatcher() .section() .item(silentModeSwitch(false)) - .item(volumeSlider(1.0)) + .item(volumeSlider(1.0, "Volume (Headphones)")) .item(mh::MenuItemMatcher() .action("indicator.high-volume-warning-item") .label("High volume can damage your hearing.") @@ -835,8 +837,8 @@ TEST_F(TestIndicator, PhoneNotificationWarningVolume) WAIT_FOR_SIGNALS(notificationsSpy, 2); // check the notification TODO check why the sound indicator sends it twice - checkVolumeNotification(0.5, "", false, notificationsSpy.at(0)); - checkVolumeNotification(0.5, "", false, notificationsSpy.at(1)); + checkVolumeNotification(0.5, "Headphones", false, notificationsSpy.at(0)); + checkVolumeNotification(0.5, "Headphones", false, notificationsSpy.at(1)); // check that the volume was applied // and that we don't have the warning item @@ -852,7 +854,7 @@ TEST_F(TestIndicator, PhoneNotificationWarningVolume) .item(mh::MenuItemMatcher() .section() .item(silentModeSwitch(false)) - .item(volumeSlider(0.5)) + .item(volumeSlider(0.5, "Volume (Headphones)")) ) ).match()); @@ -896,13 +898,14 @@ TEST_F(TestIndicator, PhoneNotificationWarningVolumeAlertMode) // change volume to 0.0... no warning should be emitted setActionValue("volume", QVariant::fromValue(0.0)); - WAIT_FOR_SIGNALS(notificationsSpy, 3); + WAIT_FOR_SIGNALS(notificationsSpy, 4); // the first time we also have the calls to // GetServerInformation and GetCapabilities checkNotificationWithNoArgs("GetServerInformation", notificationsSpy.at(0)); checkNotificationWithNoArgs("GetCapabilities", notificationsSpy.at(1)); - checkVolumeNotification(0.0, "", false, notificationsSpy.at(2)); + checkVolumeNotification(0.0, "Headphones", false, notificationsSpy.at(2)); + checkVolumeNotification(0.0, "Headphones", false, notificationsSpy.at(3)); notificationsSpy.clear(); // change volume to 0.5... no warning should be emitted @@ -910,7 +913,7 @@ TEST_F(TestIndicator, PhoneNotificationWarningVolumeAlertMode) WAIT_FOR_SIGNALS(notificationsSpy, 1); - checkVolumeNotification(0.5, "", false, notificationsSpy.at(0)); + checkVolumeNotification(0.5, "Headphones", false, notificationsSpy.at(0)); notificationsSpy.clear(); // change volume to 1.0... no warning should be emitted, we are in alert mode @@ -918,8 +921,28 @@ TEST_F(TestIndicator, PhoneNotificationWarningVolumeAlertMode) WAIT_FOR_SIGNALS(notificationsSpy, 1); - checkVolumeNotification(1.0, "", false, notificationsSpy.at(0)); + checkVolumeNotification(1.0, "Headphones", false, notificationsSpy.at(0)); notificationsSpy.clear(); } +TEST_F(TestIndicator, PhoneNotificationHeadphoneSpeakerWiredLabels) +{ + checkPortDevicesLabels(WIRED, WIRED); +} + +TEST_F(TestIndicator, PhoneNotificationHeadphoneSpeakerBluetoothLabels) +{ + checkPortDevicesLabels(BLUETOOTH, BLUETOOTH); +} + +TEST_F(TestIndicator, PhoneNotificationHeadphoneSpeakerUSBLabels) +{ + checkPortDevicesLabels(USB, USB); +} + +TEST_F(TestIndicator, PhoneNotificationHeadphoneSpeakerHDMILabels) +{ + checkPortDevicesLabels(HDMI, HDMI); +} + } // namespace diff --git a/tests/notifications-test.cc b/tests/notifications-test.cc index 8d5617d..a096bb8 100644 --- a/tests/notifications-test.cc +++ b/tests/notifications-test.cc @@ -345,7 +345,7 @@ TEST_F(NotificationsTest, HighVolume) { auto notev = notifications->getNotifications(); ASSERT_EQ(1, notev.size()); EXPECT_EQ("Volume", notev[0].summary); - EXPECT_EQ("", notev[0].body); + EXPECT_EQ("Speakers", notev[0].body); EXPECT_GVARIANT_EQ("@s 'false'", notev[0].hints["x-canonical-value-bar-tint"]); /* Set high volume with volume change */ |