From c7217944a344afc3afb48748f3b888ff7cb69cba Mon Sep 17 00:00:00 2001 From: dbuechel Date: Fri, 16 Aug 2019 10:08:10 +0200 Subject: [PATCH] SEBWIN-303: Implemented audio controls for mobile UI. --- .../Controls/ActionCenterAudioControl.xaml | 49 ++++++ .../Controls/ActionCenterAudioControl.xaml.cs | 161 +++++++++++++++++ .../Controls/TaskbarAudioControl.xaml | 43 +++++ .../Controls/TaskbarAudioControl.xaml.cs | 166 ++++++++++++++++++ .../Images/Audio_100.xaml | 13 ++ .../Images/Audio_33.xaml | 13 ++ .../Images/Audio_66.xaml | 13 ++ .../Images/Audio_Light_100.xaml | 13 ++ .../Images/Audio_Light_33.xaml | 13 ++ .../Images/Audio_Light_66.xaml | 13 ++ .../Images/Audio_Light_NoDevice.xaml | 16 ++ .../Images/Audio_Muted.xaml | 12 ++ .../Images/Audio_NoDevice.xaml | 16 ++ ...afeExamBrowser.UserInterface.Mobile.csproj | 50 ++++++ .../UserInterfaceFactory.cs | 10 +- 15 files changed, 599 insertions(+), 2 deletions(-) create mode 100644 SafeExamBrowser.UserInterface.Mobile/Controls/ActionCenterAudioControl.xaml create mode 100644 SafeExamBrowser.UserInterface.Mobile/Controls/ActionCenterAudioControl.xaml.cs create mode 100644 SafeExamBrowser.UserInterface.Mobile/Controls/TaskbarAudioControl.xaml create mode 100644 SafeExamBrowser.UserInterface.Mobile/Controls/TaskbarAudioControl.xaml.cs create mode 100644 SafeExamBrowser.UserInterface.Mobile/Images/Audio_100.xaml create mode 100644 SafeExamBrowser.UserInterface.Mobile/Images/Audio_33.xaml create mode 100644 SafeExamBrowser.UserInterface.Mobile/Images/Audio_66.xaml create mode 100644 SafeExamBrowser.UserInterface.Mobile/Images/Audio_Light_100.xaml create mode 100644 SafeExamBrowser.UserInterface.Mobile/Images/Audio_Light_33.xaml create mode 100644 SafeExamBrowser.UserInterface.Mobile/Images/Audio_Light_66.xaml create mode 100644 SafeExamBrowser.UserInterface.Mobile/Images/Audio_Light_NoDevice.xaml create mode 100644 SafeExamBrowser.UserInterface.Mobile/Images/Audio_Muted.xaml create mode 100644 SafeExamBrowser.UserInterface.Mobile/Images/Audio_NoDevice.xaml diff --git a/SafeExamBrowser.UserInterface.Mobile/Controls/ActionCenterAudioControl.xaml b/SafeExamBrowser.UserInterface.Mobile/Controls/ActionCenterAudioControl.xaml new file mode 100644 index 00000000..d15849b7 --- /dev/null +++ b/SafeExamBrowser.UserInterface.Mobile/Controls/ActionCenterAudioControl.xaml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SafeExamBrowser.UserInterface.Mobile/Controls/ActionCenterAudioControl.xaml.cs b/SafeExamBrowser.UserInterface.Mobile/Controls/ActionCenterAudioControl.xaml.cs new file mode 100644 index 00000000..9d9a22c4 --- /dev/null +++ b/SafeExamBrowser.UserInterface.Mobile/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.Mobile.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.Mobile/Controls/TaskbarAudioControl.xaml b/SafeExamBrowser.UserInterface.Mobile/Controls/TaskbarAudioControl.xaml new file mode 100644 index 00000000..ab59622d --- /dev/null +++ b/SafeExamBrowser.UserInterface.Mobile/Controls/TaskbarAudioControl.xaml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SafeExamBrowser.UserInterface.Mobile/Controls/TaskbarAudioControl.xaml.cs b/SafeExamBrowser.UserInterface.Mobile/Controls/TaskbarAudioControl.xaml.cs new file mode 100644 index 00000000..83b864ff --- /dev/null +++ b/SafeExamBrowser.UserInterface.Mobile/Controls/TaskbarAudioControl.xaml.cs @@ -0,0 +1,166 @@ +/* + * 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 SafeExamBrowser.Contracts.I18n; +using SafeExamBrowser.Contracts.UserInterface.Shell; +using SafeExamBrowser.Contracts.UserInterface.Shell.Events; +using SafeExamBrowser.UserInterface.Shared.Utilities; + +namespace SafeExamBrowser.UserInterface.Mobile.Controls +{ + public partial class TaskbarAudioControl : UserControl, ISystemAudioControl + { + private readonly IText text; + private bool muted; + private XamlIconResource MutedIcon; + private XamlIconResource NoDeviceIcon; + + public event AudioMuteRequestedEventHandler MuteRequested; + public event AudioVolumeSelectedEventHandler VolumeSelected; + + public TaskbarAudioControl(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); + } + + private void InitializeAudioControl() + { + var originalBrush = Button.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_NoDevice.xaml")); + Popup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => Popup.IsOpen = IsMouseOver)); + Volume.ValueChanged += Volume_ValueChanged; + + Popup.Opened += (o, args) => + { + Background = Brushes.LightGray; + Button.Background = Brushes.LightGray; + }; + + Popup.Closed += (o, args) => + { + Background = originalBrush; + Button.Background = originalBrush; + }; + } + + 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_{icon}.xaml"); + var resource = new XamlIconResource(uri); + + return IconResourceLoader.Load(resource); + } + } +} diff --git a/SafeExamBrowser.UserInterface.Mobile/Images/Audio_100.xaml b/SafeExamBrowser.UserInterface.Mobile/Images/Audio_100.xaml new file mode 100644 index 00000000..00883e6a --- /dev/null +++ b/SafeExamBrowser.UserInterface.Mobile/Images/Audio_100.xaml @@ -0,0 +1,13 @@ + + + + + + + + + + diff --git a/SafeExamBrowser.UserInterface.Mobile/Images/Audio_33.xaml b/SafeExamBrowser.UserInterface.Mobile/Images/Audio_33.xaml new file mode 100644 index 00000000..5e81559a --- /dev/null +++ b/SafeExamBrowser.UserInterface.Mobile/Images/Audio_33.xaml @@ -0,0 +1,13 @@ + + + + + + + + + + diff --git a/SafeExamBrowser.UserInterface.Mobile/Images/Audio_66.xaml b/SafeExamBrowser.UserInterface.Mobile/Images/Audio_66.xaml new file mode 100644 index 00000000..11b04a28 --- /dev/null +++ b/SafeExamBrowser.UserInterface.Mobile/Images/Audio_66.xaml @@ -0,0 +1,13 @@ + + + + + + + + + + diff --git a/SafeExamBrowser.UserInterface.Mobile/Images/Audio_Light_100.xaml b/SafeExamBrowser.UserInterface.Mobile/Images/Audio_Light_100.xaml new file mode 100644 index 00000000..00883e6a --- /dev/null +++ b/SafeExamBrowser.UserInterface.Mobile/Images/Audio_Light_100.xaml @@ -0,0 +1,13 @@ + + + + + + + + + + diff --git a/SafeExamBrowser.UserInterface.Mobile/Images/Audio_Light_33.xaml b/SafeExamBrowser.UserInterface.Mobile/Images/Audio_Light_33.xaml new file mode 100644 index 00000000..92834be1 --- /dev/null +++ b/SafeExamBrowser.UserInterface.Mobile/Images/Audio_Light_33.xaml @@ -0,0 +1,13 @@ + + + + + + + + + + diff --git a/SafeExamBrowser.UserInterface.Mobile/Images/Audio_Light_66.xaml b/SafeExamBrowser.UserInterface.Mobile/Images/Audio_Light_66.xaml new file mode 100644 index 00000000..f30ed5a9 --- /dev/null +++ b/SafeExamBrowser.UserInterface.Mobile/Images/Audio_Light_66.xaml @@ -0,0 +1,13 @@ + + + + + + + + + + diff --git a/SafeExamBrowser.UserInterface.Mobile/Images/Audio_Light_NoDevice.xaml b/SafeExamBrowser.UserInterface.Mobile/Images/Audio_Light_NoDevice.xaml new file mode 100644 index 00000000..96c16524 --- /dev/null +++ b/SafeExamBrowser.UserInterface.Mobile/Images/Audio_Light_NoDevice.xaml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + diff --git a/SafeExamBrowser.UserInterface.Mobile/Images/Audio_Muted.xaml b/SafeExamBrowser.UserInterface.Mobile/Images/Audio_Muted.xaml new file mode 100644 index 00000000..f5479c85 --- /dev/null +++ b/SafeExamBrowser.UserInterface.Mobile/Images/Audio_Muted.xaml @@ -0,0 +1,12 @@ + + + + + + + + + diff --git a/SafeExamBrowser.UserInterface.Mobile/Images/Audio_NoDevice.xaml b/SafeExamBrowser.UserInterface.Mobile/Images/Audio_NoDevice.xaml new file mode 100644 index 00000000..ef6abd7c --- /dev/null +++ b/SafeExamBrowser.UserInterface.Mobile/Images/Audio_NoDevice.xaml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + diff --git a/SafeExamBrowser.UserInterface.Mobile/SafeExamBrowser.UserInterface.Mobile.csproj b/SafeExamBrowser.UserInterface.Mobile/SafeExamBrowser.UserInterface.Mobile.csproj index 486d58cb..341077c8 100644 --- a/SafeExamBrowser.UserInterface.Mobile/SafeExamBrowser.UserInterface.Mobile.csproj +++ b/SafeExamBrowser.UserInterface.Mobile/SafeExamBrowser.UserInterface.Mobile.csproj @@ -82,6 +82,9 @@ ActionCenterApplicationControl.xaml + + ActionCenterAudioControl.xaml + ActionCenterClock.xaml @@ -112,6 +115,9 @@ TaskbarApplicationInstanceButton.xaml + + TaskbarAudioControl.xaml + TaskbarClock.xaml @@ -195,6 +201,10 @@ Designer MSBuild:Compile + + MSBuild:Compile + Designer + MSBuild:Compile Designer @@ -235,6 +245,10 @@ MSBuild:Compile Designer + + MSBuild:Compile + Designer + MSBuild:Compile Designer @@ -267,6 +281,42 @@ MSBuild:Compile Designer + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + MSBuild:Compile Designer diff --git a/SafeExamBrowser.UserInterface.Mobile/UserInterfaceFactory.cs b/SafeExamBrowser.UserInterface.Mobile/UserInterfaceFactory.cs index 4fdf8d08..4b5ef91d 100644 --- a/SafeExamBrowser.UserInterface.Mobile/UserInterfaceFactory.cs +++ b/SafeExamBrowser.UserInterface.Mobile/UserInterfaceFactory.cs @@ -54,8 +54,14 @@ namespace SafeExamBrowser.UserInterface.Mobile public ISystemAudioControl CreateAudioControl(Location location) { - // TODO - throw new System.NotImplementedException(); + if (location == Location.ActionCenter) + { + return new ActionCenterAudioControl(text); + } + else + { + return new TaskbarAudioControl(text); + } } public IBrowserWindow CreateBrowserWindow(IBrowserControl control, BrowserSettings settings, bool isMainWindow)