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);
}