diff --git a/SafeExamBrowser.I18n/Text.xml b/SafeExamBrowser.I18n/Text.xml index 77795678..9cbc791a 100644 --- a/SafeExamBrowser.I18n/Text.xml +++ b/SafeExamBrowser.I18n/Text.xml @@ -289,7 +289,7 @@ Click to mute audio - No audio device found + No active audio device found Click to unmute audio diff --git a/SafeExamBrowser.SystemComponents/Audio.cs b/SafeExamBrowser.SystemComponents/Audio.cs index 4fbed00c..80b0bfd3 100644 --- a/SafeExamBrowser.SystemComponents/Audio.cs +++ b/SafeExamBrowser.SystemComponents/Audio.cs @@ -175,11 +175,11 @@ namespace SafeExamBrowser.SystemComponents { try { - var info = BuildInfoText(audioDevice.AudioEndpointVolume.MasterVolumeLevelScalar, audioDevice.AudioEndpointVolume.Mute); - - foreach (var control in controls) + if (audioDevice != default(MMDevice)) { - if (audioDevice != default(MMDevice)) + var info = BuildInfoText(audioDevice.AudioEndpointVolume.MasterVolumeLevelScalar, audioDevice.AudioEndpointVolume.Mute); + + foreach (var control in controls) { control.HasOutputDevice = true; control.OutputDeviceMuted = audioDevice.AudioEndpointVolume.Mute; @@ -187,7 +187,10 @@ namespace SafeExamBrowser.SystemComponents control.OutputDeviceVolume = audioDevice.AudioEndpointVolume.MasterVolumeLevelScalar; control.SetInformation(info); } - else + } + else + { + foreach (var control in controls) { control.HasOutputDevice = false; control.SetInformation(text.Get(TextKey.SystemControl_AudioDeviceNotFound)); diff --git a/SafeExamBrowser.UserInterface.Desktop/Controls/ActionCenterAudioControl.xaml b/SafeExamBrowser.UserInterface.Desktop/Controls/ActionCenterAudioControl.xaml new file mode 100644 index 00000000..e34fdc78 --- /dev/null +++ b/SafeExamBrowser.UserInterface.Desktop/Controls/ActionCenterAudioControl.xaml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SafeExamBrowser.UserInterface.Desktop/Controls/ActionCenterAudioControl.xaml.cs b/SafeExamBrowser.UserInterface.Desktop/Controls/ActionCenterAudioControl.xaml.cs new file mode 100644 index 00000000..ac721958 --- /dev/null +++ b/SafeExamBrowser.UserInterface.Desktop/Controls/ActionCenterAudioControl.xaml.cs @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET) + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +using System; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Controls.Primitives; +using System.Windows.Media; +using System.Windows.Threading; +using SafeExamBrowser.Contracts.I18n; +using SafeExamBrowser.Contracts.UserInterface.Shell; +using SafeExamBrowser.Contracts.UserInterface.Shell.Events; +using SafeExamBrowser.UserInterface.Shared.Utilities; + +namespace SafeExamBrowser.UserInterface.Desktop.Controls +{ + public partial class ActionCenterAudioControl : UserControl, ISystemAudioControl + { + private readonly IText text; + private bool muted; + private XamlIconResource MutedIcon; + private XamlIconResource NoDeviceIcon; + + public event AudioMuteRequestedEventHandler MuteRequested; + public event AudioVolumeSelectedEventHandler VolumeSelected; + + public ActionCenterAudioControl(IText text) + { + this.text = text; + + InitializeComponent(); + InitializeAudioControl(); + } + + public bool HasOutputDevice + { + set + { + Dispatcher.InvokeAsync(() => + { + Button.IsEnabled = value; + + if (!value) + { + TaskbarIcon.Content = IconResourceLoader.Load(NoDeviceIcon); + } + }); + } + } + + public bool OutputDeviceMuted + { + set + { + Dispatcher.InvokeAsync(() => + { + muted = value; + + if (value) + { + MuteButton.ToolTip = text.Get(TextKey.SystemControl_AudioDeviceUnmuteTooltip); + PopupIcon.Content = IconResourceLoader.Load(MutedIcon); + TaskbarIcon.Content = IconResourceLoader.Load(MutedIcon); + } + else + { + MuteButton.ToolTip = text.Get(TextKey.SystemControl_AudioDeviceMuteTooltip); + TaskbarIcon.Content = LoadIcon(Volume.Value / 100); + } + }); + } + } + + public string OutputDeviceName + { + set + { + Dispatcher.InvokeAsync(() => AudioDeviceName.Text = value); + } + } + + public double OutputDeviceVolume + { + set + { + Dispatcher.InvokeAsync(() => + { + Volume.ValueChanged -= Volume_ValueChanged; + Volume.Value = Math.Round(value * 100); + Volume.ValueChanged += Volume_ValueChanged; + + if (!muted) + { + PopupIcon.Content = LoadIcon(value); + TaskbarIcon.Content = LoadIcon(value); + } + }); + } + } + + public void Close() + { + Popup.IsOpen = false; + } + + public void SetInformation(string text) + { + Dispatcher.InvokeAsync(() => + { + Button.ToolTip = text; + Text.Text = text; + }); + } + + private void InitializeAudioControl() + { + var originalBrush = Grid.Background; + + Button.Click += (o, args) => Popup.IsOpen = !Popup.IsOpen; + Button.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => Popup.IsOpen = Popup.IsMouseOver)); + MuteButton.Click += (o, args) => MuteRequested?.Invoke(!muted); + MutedIcon = new XamlIconResource(new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Desktop;component/Images/Audio_Muted.xaml")); + NoDeviceIcon = new XamlIconResource(new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Desktop;component/Images/Audio_Light_NoDevice.xaml")); + Popup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => Popup.IsOpen = IsMouseOver)); + Popup.Opened += (o, args) => Grid.Background = Brushes.Gray; + Popup.Closed += (o, args) => Grid.Background = originalBrush; + Volume.ValueChanged += Volume_ValueChanged; + } + + private void Volume_DragStarted(object sender, DragStartedEventArgs e) + { + Volume.ValueChanged -= Volume_ValueChanged; + } + + private void Volume_DragCompleted(object sender, DragCompletedEventArgs e) + { + VolumeSelected?.Invoke(Volume.Value / 100); + Volume.ValueChanged += Volume_ValueChanged; + } + + private void Volume_ValueChanged(object sender, RoutedPropertyChangedEventArgs e) + { + VolumeSelected?.Invoke(Volume.Value / 100); + } + + private UIElement LoadIcon(double volume) + { + var icon = volume > 0.66 ? "100" : (volume > 0.33 ? "66" : "33"); + var uri = new Uri($"pack://application:,,,/SafeExamBrowser.UserInterface.Desktop;component/Images/Audio_Light_{icon}.xaml"); + var resource = new XamlIconResource(uri); + + return IconResourceLoader.Load(resource); + } + } +} diff --git a/SafeExamBrowser.UserInterface.Desktop/Controls/TaskbarAudioControl.xaml.cs b/SafeExamBrowser.UserInterface.Desktop/Controls/TaskbarAudioControl.xaml.cs index 0c944b9b..b330643e 100644 --- a/SafeExamBrowser.UserInterface.Desktop/Controls/TaskbarAudioControl.xaml.cs +++ b/SafeExamBrowser.UserInterface.Desktop/Controls/TaskbarAudioControl.xaml.cs @@ -120,8 +120,8 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls Button.Click += (o, args) => Popup.IsOpen = !Popup.IsOpen; Button.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => Popup.IsOpen = Popup.IsMouseOver)); MuteButton.Click += (o, args) => MuteRequested?.Invoke(!muted); - MutedIcon = new XamlIconResource(new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Desktop;component/Images/AudioMuted.xaml")); - NoDeviceIcon = new XamlIconResource(new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Desktop;component/Images/AudioNoDevice.xaml")); + MutedIcon = new XamlIconResource(new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Desktop;component/Images/Audio_Muted.xaml")); + NoDeviceIcon = new XamlIconResource(new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Desktop;component/Images/Audio_NoDevice.xaml")); Popup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => Popup.IsOpen = IsMouseOver)); Volume.ValueChanged += Volume_ValueChanged; diff --git a/SafeExamBrowser.UserInterface.Desktop/Images/Audio_Light_100.xaml b/SafeExamBrowser.UserInterface.Desktop/Images/Audio_Light_100.xaml new file mode 100644 index 00000000..00883e6a --- /dev/null +++ b/SafeExamBrowser.UserInterface.Desktop/Images/Audio_Light_100.xaml @@ -0,0 +1,13 @@ + + + + + + + + + + diff --git a/SafeExamBrowser.UserInterface.Desktop/Images/Audio_Light_33.xaml b/SafeExamBrowser.UserInterface.Desktop/Images/Audio_Light_33.xaml new file mode 100644 index 00000000..92834be1 --- /dev/null +++ b/SafeExamBrowser.UserInterface.Desktop/Images/Audio_Light_33.xaml @@ -0,0 +1,13 @@ + + + + + + + + + + diff --git a/SafeExamBrowser.UserInterface.Desktop/Images/Audio_Light_66.xaml b/SafeExamBrowser.UserInterface.Desktop/Images/Audio_Light_66.xaml new file mode 100644 index 00000000..f30ed5a9 --- /dev/null +++ b/SafeExamBrowser.UserInterface.Desktop/Images/Audio_Light_66.xaml @@ -0,0 +1,13 @@ + + + + + + + + + + diff --git a/SafeExamBrowser.UserInterface.Desktop/Images/Audio_Light_NoDevice.xaml b/SafeExamBrowser.UserInterface.Desktop/Images/Audio_Light_NoDevice.xaml new file mode 100644 index 00000000..96c16524 --- /dev/null +++ b/SafeExamBrowser.UserInterface.Desktop/Images/Audio_Light_NoDevice.xaml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + diff --git a/SafeExamBrowser.UserInterface.Desktop/Images/AudioMuted.xaml b/SafeExamBrowser.UserInterface.Desktop/Images/Audio_Muted.xaml similarity index 100% rename from SafeExamBrowser.UserInterface.Desktop/Images/AudioMuted.xaml rename to SafeExamBrowser.UserInterface.Desktop/Images/Audio_Muted.xaml diff --git a/SafeExamBrowser.UserInterface.Desktop/Images/AudioNoDevice.xaml b/SafeExamBrowser.UserInterface.Desktop/Images/Audio_NoDevice.xaml similarity index 100% rename from SafeExamBrowser.UserInterface.Desktop/Images/AudioNoDevice.xaml rename to SafeExamBrowser.UserInterface.Desktop/Images/Audio_NoDevice.xaml diff --git a/SafeExamBrowser.UserInterface.Desktop/SafeExamBrowser.UserInterface.Desktop.csproj b/SafeExamBrowser.UserInterface.Desktop/SafeExamBrowser.UserInterface.Desktop.csproj index e5d7a063..6af87de5 100644 --- a/SafeExamBrowser.UserInterface.Desktop/SafeExamBrowser.UserInterface.Desktop.csproj +++ b/SafeExamBrowser.UserInterface.Desktop/SafeExamBrowser.UserInterface.Desktop.csproj @@ -80,6 +80,9 @@ ActionCenterApplicationButton.xaml + + ActionCenterAudioControl.xaml + ActionCenterClock.xaml @@ -175,6 +178,10 @@ Designer MSBuild:Compile + + Designer + MSBuild:Compile + Designer MSBuild:Compile @@ -263,7 +270,7 @@ MSBuild:Compile Designer - + MSBuild:Compile Designer @@ -279,7 +286,23 @@ MSBuild:Compile Designer - + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + MSBuild:Compile Designer diff --git a/SafeExamBrowser.UserInterface.Desktop/UserInterfaceFactory.cs b/SafeExamBrowser.UserInterface.Desktop/UserInterfaceFactory.cs index 394253a3..078a912c 100644 --- a/SafeExamBrowser.UserInterface.Desktop/UserInterfaceFactory.cs +++ b/SafeExamBrowser.UserInterface.Desktop/UserInterfaceFactory.cs @@ -54,12 +54,11 @@ namespace SafeExamBrowser.UserInterface.Desktop public ISystemAudioControl CreateAudioControl(Location location) { - // TODO - //if (location == Location.ActionCenter) - //{ - // return new ActionCenterAudioControl(); - //} - //else + if (location == Location.ActionCenter) + { + return new ActionCenterAudioControl(text); + } + else { return new TaskbarAudioControl(text); }