diff --git a/SafeExamBrowser.Client.UnitTests/Operations/ShellOperationTests.cs b/SafeExamBrowser.Client.UnitTests/Operations/ShellOperationTests.cs index b0f31e56..022a7b4a 100644 --- a/SafeExamBrowser.Client.UnitTests/Operations/ShellOperationTests.cs +++ b/SafeExamBrowser.Client.UnitTests/Operations/ShellOperationTests.cs @@ -34,6 +34,7 @@ namespace SafeExamBrowser.Client.UnitTests.Operations private Mock aboutController; private Mock logInfo; private Mock logController; + private Mock> audio; private Mock> keyboardLayout; private Mock> powerSupply; private Mock> wirelessNetwork; @@ -55,6 +56,7 @@ namespace SafeExamBrowser.Client.UnitTests.Operations aboutController = new Mock(); logInfo = new Mock(); logController = new Mock(); + audio = new Mock>(); keyboardLayout = new Mock>(); powerSupply = new Mock>(); wirelessNetwork = new Mock>(); @@ -76,6 +78,7 @@ namespace SafeExamBrowser.Client.UnitTests.Operations aboutController.Object, logInfo.Object, logController.Object, + audio.Object, keyboardLayout.Object, powerSupply.Object, wirelessNetwork.Object, diff --git a/SafeExamBrowser.Client/CompositionRoot.cs b/SafeExamBrowser.Client/CompositionRoot.cs index f8f6c06c..d9ef0876 100644 --- a/SafeExamBrowser.Client/CompositionRoot.cs +++ b/SafeExamBrowser.Client/CompositionRoot.cs @@ -67,6 +67,7 @@ namespace SafeExamBrowser.Client private IProcessMonitor processMonitor; private INativeMethods nativeMethods; private IRuntimeProxy runtimeProxy; + private ISystemComponent audio; private ISystemComponent keyboardLayout; private ISystemComponent powerSupply; private ISystemComponent wirelessNetwork; @@ -93,6 +94,7 @@ namespace SafeExamBrowser.Client InitializeText(); actionCenter = BuildActionCenter(); + audio = new Audio(new ModuleLogger(logger, nameof(Audio)), text); keyboardLayout = new KeyboardLayout(new ModuleLogger(logger, nameof(KeyboardLayout)), text); messageBox = BuildMessageBox(); powerSupply = new PowerSupply(new ModuleLogger(logger, nameof(PowerSupply)), text); @@ -277,6 +279,7 @@ namespace SafeExamBrowser.Client aboutController, logInfo, logController, + audio, keyboardLayout, powerSupply, wirelessNetwork, diff --git a/SafeExamBrowser.Client/Operations/ShellOperation.cs b/SafeExamBrowser.Client/Operations/ShellOperation.cs index b9e929ab..4b096289 100644 --- a/SafeExamBrowser.Client/Operations/ShellOperation.cs +++ b/SafeExamBrowser.Client/Operations/ShellOperation.cs @@ -30,6 +30,7 @@ namespace SafeExamBrowser.Client.Operations private INotificationController aboutController; private INotificationInfo logInfo; private INotificationController logController; + private ISystemComponent audio; private ISystemComponent keyboardLayout; private ISystemComponent powerSupply; private ISystemComponent wirelessNetwork; @@ -52,6 +53,7 @@ namespace SafeExamBrowser.Client.Operations INotificationController aboutController, INotificationInfo logInfo, INotificationController logController, + ISystemComponent audio, ISystemComponent keyboardLayout, ISystemComponent powerSupply, ISystemComponent wirelessNetwork, @@ -70,6 +72,7 @@ namespace SafeExamBrowser.Client.Operations this.logger = logger; this.logInfo = logInfo; this.logController = logController; + this.audio = audio; this.keyboardLayout = keyboardLayout; this.powerSupply = powerSupply; this.systemInfo = systemInfo; @@ -128,6 +131,7 @@ namespace SafeExamBrowser.Client.Operations actionCenter.InitializeText(text); InitializeAboutNotificationForActionCenter(); + InitializeAudioForActionCenter(); InitializeClockForActionCenter(); InitializeLogNotificationForActionCenter(); InitializeKeyboardLayoutForActionCenter(); @@ -148,11 +152,12 @@ namespace SafeExamBrowser.Client.Operations taskbar.InitializeText(text); InitializeAboutNotificationForTaskbar(); - InitializeClockForTaskbar(); InitializeLogNotificationForTaskbar(); - InitializeKeyboardLayoutForTaskbar(); - InitializeWirelessNetworkForTaskbar(); InitializePowerSupplyForTaskbar(); + InitializeWirelessNetworkForTaskbar(); + InitializeAudioForTaskbar(); + InitializeKeyboardLayoutForTaskbar(); + InitializeClockForTaskbar(); } else { @@ -162,6 +167,7 @@ namespace SafeExamBrowser.Client.Operations private void InitializeSystemComponents() { + audio.Initialize(); keyboardLayout.Initialize(); powerSupply.Initialize(); wirelessNetwork.Initialize(); @@ -189,6 +195,28 @@ namespace SafeExamBrowser.Client.Operations } } + private void InitializeAudioForActionCenter() + { + if (actionCenterSettings.ShowAudio) + { + var control = uiFactory.CreateAudioControl(Location.ActionCenter); + + audio.Register(control); + actionCenter.AddSystemControl(control); + } + } + + private void InitializeAudioForTaskbar() + { + if (taskbarSettings.ShowAudio) + { + var control = uiFactory.CreateAudioControl(Location.Taskbar); + + audio.Register(control); + taskbar.AddSystemControl(control); + } + } + private void InitializeClockForActionCenter() { actionCenter.ShowClock = actionCenterSettings.ShowClock; @@ -308,6 +336,7 @@ namespace SafeExamBrowser.Client.Operations private void TerminateSystemComponents() { + audio.Terminate(); keyboardLayout.Terminate(); powerSupply.Terminate(); wirelessNetwork.Terminate(); diff --git a/SafeExamBrowser.Configuration/ConfigurationData/DataMapper.UserInterface.cs b/SafeExamBrowser.Configuration/ConfigurationData/DataMapper.UserInterface.cs index bcbf3522..20c76b79 100644 --- a/SafeExamBrowser.Configuration/ConfigurationData/DataMapper.UserInterface.cs +++ b/SafeExamBrowser.Configuration/ConfigurationData/DataMapper.UserInterface.cs @@ -20,6 +20,15 @@ namespace SafeExamBrowser.Configuration.ConfigurationData } } + private void MapAudio(Settings settings, object value) + { + if (value is bool show) + { + settings.ActionCenter.ShowAudio = show; + settings.Taskbar.ShowAudio = show; + } + } + private void MapClock(Settings settings, object value) { if (value is bool show) diff --git a/SafeExamBrowser.Configuration/ConfigurationData/DataMapper.cs b/SafeExamBrowser.Configuration/ConfigurationData/DataMapper.cs index 5918021a..d6f35d9d 100644 --- a/SafeExamBrowser.Configuration/ConfigurationData/DataMapper.cs +++ b/SafeExamBrowser.Configuration/ConfigurationData/DataMapper.cs @@ -189,6 +189,9 @@ namespace SafeExamBrowser.Configuration.ConfigurationData { switch (key) { + case Keys.UserInterface.ShowAudio: + MapAudio(settings, value); + break; case Keys.UserInterface.ShowKeyboardLayout: MapKeyboardLayout(settings, value); break; diff --git a/SafeExamBrowser.Configuration/ConfigurationData/Keys.cs b/SafeExamBrowser.Configuration/ConfigurationData/Keys.cs index bc6953f3..b10c1fb4 100644 --- a/SafeExamBrowser.Configuration/ConfigurationData/Keys.cs +++ b/SafeExamBrowser.Configuration/ConfigurationData/Keys.cs @@ -121,6 +121,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData internal static class UserInterface { + internal const string ShowAudio = "audioControlEnabled"; internal const string ShowClock = "showTime"; internal const string ShowKeyboardLayout = "showInputLanguage"; internal const string ShowWirelessNetwork = "allowWlan"; diff --git a/SafeExamBrowser.Contracts/Configuration/Settings/ActionCenterSettings.cs b/SafeExamBrowser.Contracts/Configuration/Settings/ActionCenterSettings.cs index dc27e50f..9656c915 100644 --- a/SafeExamBrowser.Contracts/Configuration/Settings/ActionCenterSettings.cs +++ b/SafeExamBrowser.Contracts/Configuration/Settings/ActionCenterSettings.cs @@ -31,6 +31,11 @@ namespace SafeExamBrowser.Contracts.Configuration.Settings /// public bool ShowApplicationLog { get; set; } + /// + /// Determines whether the system control for audio is accessible via the action center. + /// + public bool ShowAudio { get; set; } + /// /// Determines whether the current date and time will be rendered in the action center. /// diff --git a/SafeExamBrowser.Contracts/Configuration/Settings/TaskbarSettings.cs b/SafeExamBrowser.Contracts/Configuration/Settings/TaskbarSettings.cs index a9939cc1..c31682a5 100644 --- a/SafeExamBrowser.Contracts/Configuration/Settings/TaskbarSettings.cs +++ b/SafeExamBrowser.Contracts/Configuration/Settings/TaskbarSettings.cs @@ -31,6 +31,11 @@ namespace SafeExamBrowser.Contracts.Configuration.Settings /// public bool ShowApplicationLog { get; set; } + /// + /// Determines whether the system control for audio is accessible via the taskbar. + /// + public bool ShowAudio { get; set; } + /// /// Determines whether the current date and time will be rendered in the taskbar. /// diff --git a/SafeExamBrowser.Contracts/I18n/TextKey.cs b/SafeExamBrowser.Contracts/I18n/TextKey.cs index 063db73e..abd23e2c 100644 --- a/SafeExamBrowser.Contracts/I18n/TextKey.cs +++ b/SafeExamBrowser.Contracts/I18n/TextKey.cs @@ -107,6 +107,9 @@ namespace SafeExamBrowser.Contracts.I18n PasswordDialog_SettingsPasswordRequiredTitle, RuntimeWindow_ApplicationRunning, Shell_QuitButton, + SystemControl_AudioDeviceInfo, + SystemControl_AudioDeviceInfoMuted, + SystemControl_AudioDeviceNotFound, SystemControl_BatteryCharged, SystemControl_BatteryCharging, SystemControl_BatteryChargeCriticalWarning, diff --git a/SafeExamBrowser.Contracts/SafeExamBrowser.Contracts.csproj b/SafeExamBrowser.Contracts/SafeExamBrowser.Contracts.csproj index 258ebc73..dae5766e 100644 --- a/SafeExamBrowser.Contracts/SafeExamBrowser.Contracts.csproj +++ b/SafeExamBrowser.Contracts/SafeExamBrowser.Contracts.csproj @@ -212,6 +212,7 @@ + diff --git a/SafeExamBrowser.Contracts/SystemComponents/ISystemComponent.cs b/SafeExamBrowser.Contracts/SystemComponents/ISystemComponent.cs index 1b721735..4157298d 100644 --- a/SafeExamBrowser.Contracts/SystemComponents/ISystemComponent.cs +++ b/SafeExamBrowser.Contracts/SystemComponents/ISystemComponent.cs @@ -11,8 +11,8 @@ using SafeExamBrowser.Contracts.UserInterface.Shell; namespace SafeExamBrowser.Contracts.SystemComponents { /// - /// Defines the functionality of a system component (e.g. the power supply). Each system component will get an - /// assigned, via which the user is able to interact with or get information about the underlying system component. + /// Defines the functionality of a system component (e.g. the power supply). Each system component can get multiple + /// assigned, which in turn allow the user to interact with or get information about the underlying system component. /// public interface ISystemComponent where TControl : ISystemControl { diff --git a/SafeExamBrowser.Contracts/UserInterface/IUserInterfaceFactory.cs b/SafeExamBrowser.Contracts/UserInterface/IUserInterfaceFactory.cs index 81e6550f..2c38a00b 100644 --- a/SafeExamBrowser.Contracts/UserInterface/IUserInterfaceFactory.cs +++ b/SafeExamBrowser.Contracts/UserInterface/IUserInterfaceFactory.cs @@ -33,6 +33,11 @@ namespace SafeExamBrowser.Contracts.UserInterface /// IApplicationControl CreateApplicationControl(IApplicationInfo info, Location location); + /// + /// Creates a system control which allows to change the audio settings of the computer. + /// + ISystemAudioControl CreateAudioControl(Location location); + /// /// Creates a new browser window loaded with the given browser control and settings. /// diff --git a/SafeExamBrowser.Contracts/UserInterface/Shell/ISystemAudioControl.cs b/SafeExamBrowser.Contracts/UserInterface/Shell/ISystemAudioControl.cs new file mode 100644 index 00000000..f78b11ed --- /dev/null +++ b/SafeExamBrowser.Contracts/UserInterface/Shell/ISystemAudioControl.cs @@ -0,0 +1,36 @@ +/* + * 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/. + */ + +namespace SafeExamBrowser.Contracts.UserInterface.Shell +{ + /// + /// The control of the audio system component. + /// + public interface ISystemAudioControl : ISystemControl + { + /// + /// Defines whether the computer has an audio output device. + /// + bool HasOutputDevice { set; } + + /// + /// Indicates whether the current output device is muted. + /// + bool OutputDeviceMuted { set; } + + /// + /// Shows the name of the currently active audio output device. + /// + string OutputDeviceName { set; } + + /// + /// Shows the current audio output volume, where 0.0 is the lowest and 1.0 the highest possible value. + /// + double OutputDeviceVolume { set; } + } +} diff --git a/SafeExamBrowser.I18n/Text.xml b/SafeExamBrowser.I18n/Text.xml index 921d9595..9445d13e 100644 --- a/SafeExamBrowser.I18n/Text.xml +++ b/SafeExamBrowser.I18n/Text.xml @@ -279,6 +279,15 @@ Terminate Session + + %%NAME%%: %%VOLUME%%% + + + %%NAME%%: Muted + + + No audio device found + Plugged in, charging... (%%CHARGE%%%) diff --git a/SafeExamBrowser.SystemComponents/Audio.cs b/SafeExamBrowser.SystemComponents/Audio.cs new file mode 100644 index 00000000..93b78114 --- /dev/null +++ b/SafeExamBrowser.SystemComponents/Audio.cs @@ -0,0 +1,162 @@ +/* + * 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.Collections.Generic; +using System.Linq; +using NAudio.CoreAudioApi; +using SafeExamBrowser.Contracts.I18n; +using SafeExamBrowser.Contracts.Logging; +using SafeExamBrowser.Contracts.SystemComponents; +using SafeExamBrowser.Contracts.UserInterface.Shell; + +namespace SafeExamBrowser.SystemComponents +{ + public class Audio : ISystemComponent + { + private readonly object @lock = new object(); + + private MMDevice audioDevice; + private string audioDeviceShortName; + private List controls; + private ILogger logger; + private IText text; + + public Audio(ILogger logger, IText text) + { + this.controls = new List(); + this.logger = logger; + this.text = text; + } + + public void Initialize() + { + if (TryLoadAudioDevice()) + { + InitializeAudioDevice(); + InitializeSettings(); + } + else + { + logger.Warn("Could not find an active audio device!"); + } + } + + public void Register(ISystemAudioControl control) + { + lock (@lock) + { + controls.Add(control); + } + + UpdateControls(); + } + + public void Terminate() + { + if (audioDevice != default(MMDevice)) + { + audioDevice.AudioEndpointVolume.OnVolumeNotification -= AudioEndpointVolume_OnVolumeNotification; + audioDevice.Dispose(); + logger.Info("Stopped monitoring the audio device."); + } + + foreach (var control in controls) + { + control.Close(); + } + } + + private bool TryLoadAudioDevice() + { + using (var enumerator = new MMDeviceEnumerator()) + { + if (enumerator.HasDefaultAudioEndpoint(DataFlow.Render, Role.Console)) + { + audioDevice = enumerator.GetDefaultAudioEndpoint(DataFlow.Render, Role.Console); + } + else + { + audioDevice = enumerator.EnumerateAudioEndPoints(DataFlow.Render, DeviceState.Active).FirstOrDefault(); + } + } + + return audioDevice != default(MMDevice); + } + + private void InitializeAudioDevice() + { + logger.Info($"Found '{audioDevice}' to be the active audio device."); + audioDevice.AudioEndpointVolume.OnVolumeNotification += AudioEndpointVolume_OnVolumeNotification; + audioDeviceShortName = audioDevice.FriendlyName.Length > 25 ? audioDevice.FriendlyName.Split(' ').First() : audioDevice.FriendlyName; + logger.Info("Started monitoring the audio device."); + } + + private void InitializeSettings() + { + // TODO: Mute on startup & initial volume! + } + + private void AudioEndpointVolume_OnVolumeNotification(AudioVolumeNotificationData data) + { + lock (@lock) + { + var info = BuildInfoText(data.MasterVolume, data.Muted); + + foreach (var control in controls) + { + control.OutputDeviceMuted = data.Muted; + control.OutputDeviceVolume = data.MasterVolume; + control.SetInformation(info); + } + } + } + + private void UpdateControls() + { + lock (@lock) + { + try + { + var info = BuildInfoText(audioDevice.AudioEndpointVolume.MasterVolumeLevelScalar, audioDevice.AudioEndpointVolume.Mute); + + foreach (var control in controls) + { + if (audioDevice != default(MMDevice)) + { + control.HasOutputDevice = true; + control.OutputDeviceMuted = audioDevice.AudioEndpointVolume.Mute; + control.OutputDeviceName = audioDevice.FriendlyName; + control.OutputDeviceVolume = audioDevice.AudioEndpointVolume.MasterVolumeLevelScalar; + control.SetInformation(info); + } + else + { + control.HasOutputDevice = false; + control.SetInformation(text.Get(TextKey.SystemControl_AudioDeviceNotFound)); + } + } + } + catch (Exception e) + { + logger.Error("Failed to update audio device status!", e); + } + } + } + + private string BuildInfoText(float volume, bool muted) + { + var info = text.Get(muted ? TextKey.SystemControl_AudioDeviceInfoMuted : TextKey.SystemControl_AudioDeviceInfo); + + info = info.Replace("%%NAME%%", audioDeviceShortName); + info = info.Replace("%%VOLUME%%", Convert.ToString(volume * 100)); + + return info; + } + } +} diff --git a/SafeExamBrowser.SystemComponents/SafeExamBrowser.SystemComponents.csproj b/SafeExamBrowser.SystemComponents/SafeExamBrowser.SystemComponents.csproj index 358078b7..2954a0f4 100644 --- a/SafeExamBrowser.SystemComponents/SafeExamBrowser.SystemComponents.csproj +++ b/SafeExamBrowser.SystemComponents/SafeExamBrowser.SystemComponents.csproj @@ -48,6 +48,9 @@ prompt + + ..\packages\NAudio.1.9.0\lib\net35\NAudio.dll + False @@ -59,6 +62,7 @@ + @@ -74,5 +78,8 @@ SafeExamBrowser.Contracts + + + \ No newline at end of file diff --git a/SafeExamBrowser.SystemComponents/packages.config b/SafeExamBrowser.SystemComponents/packages.config new file mode 100644 index 00000000..d964d736 --- /dev/null +++ b/SafeExamBrowser.SystemComponents/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/SafeExamBrowser.UserInterface.Desktop/Controls/TaskbarAudioControl.xaml b/SafeExamBrowser.UserInterface.Desktop/Controls/TaskbarAudioControl.xaml new file mode 100644 index 00000000..505b3356 --- /dev/null +++ b/SafeExamBrowser.UserInterface.Desktop/Controls/TaskbarAudioControl.xaml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SafeExamBrowser.UserInterface.Desktop/Controls/TaskbarAudioControl.xaml.cs b/SafeExamBrowser.UserInterface.Desktop/Controls/TaskbarAudioControl.xaml.cs new file mode 100644 index 00000000..3f036f42 --- /dev/null +++ b/SafeExamBrowser.UserInterface.Desktop/Controls/TaskbarAudioControl.xaml.cs @@ -0,0 +1,135 @@ +/* + * 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.Media; +using SafeExamBrowser.Contracts.UserInterface.Shell; +using SafeExamBrowser.UserInterface.Shared.Utilities; + +namespace SafeExamBrowser.UserInterface.Desktop.Controls +{ + public partial class TaskbarAudioControl : UserControl, ISystemAudioControl + { + private bool muted; + private XamlIconResource MutedIcon; + private XamlIconResource NoDeviceIcon; + + public TaskbarAudioControl() + { + 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) + { + PopupIcon.Content = IconResourceLoader.Load(MutedIcon); + TaskbarIcon.Content = IconResourceLoader.Load(MutedIcon); + } + else + { + TaskbarIcon.Content = LoadIcon(Volume.Value / 100); + } + }); + } + } + + public string OutputDeviceName + { + set + { + Dispatcher.InvokeAsync(() => AudioDeviceName.Text = value); + } + } + + public double OutputDeviceVolume + { + set + { + Dispatcher.InvokeAsync(() => + { + Volume.Value = Math.Round(value * 100); + + 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)); + 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")); + Popup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => Popup.IsOpen = IsMouseOver)); + + Popup.Opened += (o, args) => + { + Background = Brushes.LightGray; + Button.Background = Brushes.LightGray; + }; + + Popup.Closed += (o, args) => + { + Background = originalBrush; + Button.Background = originalBrush; + }; + } + + 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.Desktop/Images/AudioMuted.xaml b/SafeExamBrowser.UserInterface.Desktop/Images/AudioMuted.xaml new file mode 100644 index 00000000..f5479c85 --- /dev/null +++ b/SafeExamBrowser.UserInterface.Desktop/Images/AudioMuted.xaml @@ -0,0 +1,12 @@ + + + + + + + + + diff --git a/SafeExamBrowser.UserInterface.Desktop/Images/AudioNoDevice.xaml b/SafeExamBrowser.UserInterface.Desktop/Images/AudioNoDevice.xaml new file mode 100644 index 00000000..ef6abd7c --- /dev/null +++ b/SafeExamBrowser.UserInterface.Desktop/Images/AudioNoDevice.xaml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + diff --git a/SafeExamBrowser.UserInterface.Desktop/Images/Audio_100.xaml b/SafeExamBrowser.UserInterface.Desktop/Images/Audio_100.xaml new file mode 100644 index 00000000..00883e6a --- /dev/null +++ b/SafeExamBrowser.UserInterface.Desktop/Images/Audio_100.xaml @@ -0,0 +1,13 @@ + + + + + + + + + + diff --git a/SafeExamBrowser.UserInterface.Desktop/Images/Audio_33.xaml b/SafeExamBrowser.UserInterface.Desktop/Images/Audio_33.xaml new file mode 100644 index 00000000..5e81559a --- /dev/null +++ b/SafeExamBrowser.UserInterface.Desktop/Images/Audio_33.xaml @@ -0,0 +1,13 @@ + + + + + + + + + + diff --git a/SafeExamBrowser.UserInterface.Desktop/Images/Audio_66.xaml b/SafeExamBrowser.UserInterface.Desktop/Images/Audio_66.xaml new file mode 100644 index 00000000..11b04a28 --- /dev/null +++ b/SafeExamBrowser.UserInterface.Desktop/Images/Audio_66.xaml @@ -0,0 +1,13 @@ + + + + + + + + + + diff --git a/SafeExamBrowser.UserInterface.Desktop/SafeExamBrowser.UserInterface.Desktop.csproj b/SafeExamBrowser.UserInterface.Desktop/SafeExamBrowser.UserInterface.Desktop.csproj index 45c21860..e5d7a063 100644 --- a/SafeExamBrowser.UserInterface.Desktop/SafeExamBrowser.UserInterface.Desktop.csproj +++ b/SafeExamBrowser.UserInterface.Desktop/SafeExamBrowser.UserInterface.Desktop.csproj @@ -110,6 +110,9 @@ TaskbarApplicationInstanceButton.xaml + + TaskbarAudioControl.xaml + TaskbarClock.xaml @@ -212,6 +215,10 @@ Designer MSBuild:Compile + + Designer + MSBuild:Compile + Designer MSBuild:Compile @@ -256,6 +263,26 @@ MSBuild:Compile Designer + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + Designer MSBuild:Compile diff --git a/SafeExamBrowser.UserInterface.Desktop/UserInterfaceFactory.cs b/SafeExamBrowser.UserInterface.Desktop/UserInterfaceFactory.cs index f4da5966..5632742f 100644 --- a/SafeExamBrowser.UserInterface.Desktop/UserInterfaceFactory.cs +++ b/SafeExamBrowser.UserInterface.Desktop/UserInterfaceFactory.cs @@ -52,6 +52,19 @@ namespace SafeExamBrowser.UserInterface.Desktop } } + public ISystemAudioControl CreateAudioControl(Location location) + { + // TODO + //if (location == Location.ActionCenter) + //{ + // return new ActionCenterAudioControl(); + //} + //else + { + return new TaskbarAudioControl(); + } + } + public IBrowserWindow CreateBrowserWindow(IBrowserControl control, BrowserSettings settings, bool isMainWindow) { return new BrowserWindow(control, settings, isMainWindow, text); diff --git a/SafeExamBrowser.UserInterface.Mobile/UserInterfaceFactory.cs b/SafeExamBrowser.UserInterface.Mobile/UserInterfaceFactory.cs index 424868e7..4fdf8d08 100644 --- a/SafeExamBrowser.UserInterface.Mobile/UserInterfaceFactory.cs +++ b/SafeExamBrowser.UserInterface.Mobile/UserInterfaceFactory.cs @@ -52,6 +52,12 @@ namespace SafeExamBrowser.UserInterface.Mobile } } + public ISystemAudioControl CreateAudioControl(Location location) + { + // TODO + throw new System.NotImplementedException(); + } + public IBrowserWindow CreateBrowserWindow(IBrowserControl control, BrowserSettings settings, bool isMainWindow) { return new BrowserWindow(control, settings, isMainWindow, text); diff --git a/Setup/Resources/License.rtf b/Setup/Resources/License.rtf index a84f06bd..d44fbda4 100644 Binary files a/Setup/Resources/License.rtf and b/Setup/Resources/License.rtf differ