diff --git a/SafeExamBrowser.Client.UnitTests/Operations/ShellOperationTests.cs b/SafeExamBrowser.Client.UnitTests/Operations/ShellOperationTests.cs index 8175c343..12ff42fc 100644 --- a/SafeExamBrowser.Client.UnitTests/Operations/ShellOperationTests.cs +++ b/SafeExamBrowser.Client.UnitTests/Operations/ShellOperationTests.cs @@ -17,6 +17,7 @@ using SafeExamBrowser.Logging.Contracts; using SafeExamBrowser.SystemComponents.Contracts; using SafeExamBrowser.SystemComponents.Contracts.Audio; using SafeExamBrowser.SystemComponents.Contracts.Keyboard; +using SafeExamBrowser.SystemComponents.Contracts.PowerSupply; using SafeExamBrowser.UserInterface.Contracts; using SafeExamBrowser.UserInterface.Contracts.Shell; using SafeExamBrowser.WindowsApi.Contracts; @@ -38,8 +39,8 @@ namespace SafeExamBrowser.Client.UnitTests.Operations private Mock keyboard; private Mock logInfo; private Mock logController; + private Mock powerSupply; // TODO - //private Mock> powerSupply; //private Mock> wirelessNetwork; private Mock systemInfo; private Mock taskbar; @@ -61,8 +62,8 @@ namespace SafeExamBrowser.Client.UnitTests.Operations keyboard = new Mock(); logInfo = new Mock(); logController = new Mock(); + powerSupply = new Mock(); // TODO - //powerSupply = new Mock>(); //wirelessNetwork = new Mock>(); systemInfo = new Mock(); taskbar = new Mock(); @@ -84,9 +85,8 @@ namespace SafeExamBrowser.Client.UnitTests.Operations keyboard.Object, logInfo.Object, logController.Object, + powerSupply.Object, // TODO - //audio.Object, - //powerSupply.Object, //wirelessNetwork.Object, systemInfo.Object, taskbar.Object, @@ -200,14 +200,14 @@ namespace SafeExamBrowser.Client.UnitTests.Operations systemInfo.SetupGet(s => s.HasBattery).Returns(true); uiFactory.Setup(f => f.CreateAudioControl(It.IsAny(), It.IsAny())).Returns(new Mock().Object); uiFactory.Setup(f => f.CreateKeyboardLayoutControl(It.IsAny(), It.IsAny())).Returns(new Mock().Object); - uiFactory.Setup(f => f.CreatePowerSupplyControl(It.IsAny())).Returns(new Mock().Object); + uiFactory.Setup(f => f.CreatePowerSupplyControl(It.IsAny(), It.IsAny())).Returns(new Mock().Object); uiFactory.Setup(f => f.CreateWirelessNetworkControl(It.IsAny())).Returns(new Mock().Object); sut.Perform(); + audio.Verify(a => a.Initialize(), Times.Once); + powerSupply.Verify(p => p.Initialize(), Times.Once); // TODO - //audio.Verify(a => a.Initialize(), Times.Once); - //powerSupply.Verify(p => p.Initialize(), Times.Once); //wirelessNetwork.Verify(w => w.Initialize(), Times.Once); keyboard.Verify(k => k.Initialize(), Times.Once); actionCenter.Verify(a => a.AddSystemControl(It.IsAny()), Times.Exactly(4)); @@ -229,14 +229,14 @@ namespace SafeExamBrowser.Client.UnitTests.Operations systemInfo.SetupGet(s => s.HasBattery).Returns(false); uiFactory.Setup(f => f.CreateAudioControl(It.IsAny(), It.IsAny())).Returns(new Mock().Object); uiFactory.Setup(f => f.CreateKeyboardLayoutControl(It.IsAny(), It.IsAny())).Returns(new Mock().Object); - uiFactory.Setup(f => f.CreatePowerSupplyControl(It.IsAny())).Returns(new Mock().Object); + uiFactory.Setup(f => f.CreatePowerSupplyControl(It.IsAny(), It.IsAny())).Returns(new Mock().Object); uiFactory.Setup(f => f.CreateWirelessNetworkControl(It.IsAny())).Returns(new Mock().Object); sut.Perform(); + audio.Verify(a => a.Initialize(), Times.Once); + powerSupply.Verify(p => p.Initialize(), Times.Once); // TODO - //audio.Verify(a => a.Initialize(), Times.Once); - //powerSupply.Verify(p => p.Initialize(), Times.Once); //wirelessNetwork.Verify(w => w.Initialize(), Times.Once); keyboard.Verify(k => k.Initialize(), Times.Once); actionCenter.Verify(a => a.AddSystemControl(It.IsAny()), Times.Never); @@ -292,11 +292,11 @@ namespace SafeExamBrowser.Client.UnitTests.Operations sut.Revert(); aboutController.Verify(c => c.Terminate(), Times.Once); + audio.Verify(a => a.Terminate(), Times.Once); logController.Verify(c => c.Terminate(), Times.Once); - // TODO - //audio.Verify(a => a.Terminate(), Times.Once); + powerSupply.Verify(p => p.Terminate(), Times.Once); keyboard.Verify(k => k.Terminate(), Times.Once); - //powerSupply.Verify(p => p.Terminate(), Times.Once); + // TODO //wirelessNetwork.Verify(w => w.Terminate(), Times.Once); } } diff --git a/SafeExamBrowser.Client/CompositionRoot.cs b/SafeExamBrowser.Client/CompositionRoot.cs index 60b66e6f..f8b52f13 100644 --- a/SafeExamBrowser.Client/CompositionRoot.cs +++ b/SafeExamBrowser.Client/CompositionRoot.cs @@ -42,6 +42,7 @@ using SafeExamBrowser.SystemComponents; using SafeExamBrowser.SystemComponents.Audio; using SafeExamBrowser.SystemComponents.Contracts; using SafeExamBrowser.SystemComponents.Keyboard; +using SafeExamBrowser.SystemComponents.PowerSupply; using SafeExamBrowser.UserInterface.Contracts; using SafeExamBrowser.UserInterface.Contracts.MessageBox; using SafeExamBrowser.UserInterface.Contracts.Shell; @@ -257,11 +258,11 @@ namespace SafeExamBrowser.Client { var aboutInfo = new AboutNotificationInfo(text); var aboutController = new AboutNotificationController(configuration.AppConfig, uiFactory); - var audio = new Audio(configuration.Settings.Audio, new ModuleLogger(logger, nameof(Audio)), text); - var keyboard = new Keyboard(new ModuleLogger(logger, nameof(Keyboard)), text); + var audio = new Audio(configuration.Settings.Audio, new ModuleLogger(logger, nameof(Audio))); + var keyboard = new Keyboard(new ModuleLogger(logger, nameof(Keyboard))); var logInfo = new LogNotificationInfo(text); var logController = new LogNotificationController(logger, uiFactory); - var powerSupply = new PowerSupply(new ModuleLogger(logger, nameof(PowerSupply)), text); + var powerSupply = new PowerSupply(new ModuleLogger(logger, nameof(PowerSupply))); var wirelessNetwork = new WirelessNetwork(new ModuleLogger(logger, nameof(WirelessNetwork)), text); var activators = new IActionCenterActivator[] { @@ -279,8 +280,8 @@ namespace SafeExamBrowser.Client keyboard, logInfo, logController, + powerSupply, // TODO - //powerSupply, //wirelessNetwork, systemInfo, taskbar, diff --git a/SafeExamBrowser.Client/Operations/ShellOperation.cs b/SafeExamBrowser.Client/Operations/ShellOperation.cs index 7c33141a..4ab573e8 100644 --- a/SafeExamBrowser.Client/Operations/ShellOperation.cs +++ b/SafeExamBrowser.Client/Operations/ShellOperation.cs @@ -16,6 +16,7 @@ using SafeExamBrowser.Logging.Contracts; using SafeExamBrowser.SystemComponents.Contracts; using SafeExamBrowser.SystemComponents.Contracts.Audio; using SafeExamBrowser.SystemComponents.Contracts.Keyboard; +using SafeExamBrowser.SystemComponents.Contracts.PowerSupply; using SafeExamBrowser.UserInterface.Contracts; using SafeExamBrowser.UserInterface.Contracts.Shell; using SafeExamBrowser.WindowsApi.Contracts; @@ -34,7 +35,7 @@ namespace SafeExamBrowser.Client.Operations private IKeyboard keyboard; private INotificationInfo logInfo; private INotificationController logController; - // TODO private ISystemComponent powerSupply; + private IPowerSupply powerSupply; // TODO private ISystemComponent wirelessNetwork; private ISystemInfo systemInfo; private ITaskbar taskbar; @@ -57,7 +58,7 @@ namespace SafeExamBrowser.Client.Operations IKeyboard keyboard, INotificationInfo logInfo, INotificationController logController, - // TODO ISystemComponent powerSupply, + IPowerSupply powerSupply, // TODO ISystemComponent wirelessNetwork, ISystemInfo systemInfo, ITaskbar taskbar, @@ -76,7 +77,7 @@ namespace SafeExamBrowser.Client.Operations this.logger = logger; this.logInfo = logInfo; this.logController = logController; - // TODO this.powerSupply = powerSupply; + this.powerSupply = powerSupply; this.systemInfo = systemInfo; this.taskbarSettings = taskbarSettings; this.terminationActivator = terminationActivator; @@ -171,8 +172,8 @@ namespace SafeExamBrowser.Client.Operations { audio.Initialize(); keyboard.Initialize(); + powerSupply.Initialize(); // TODO - //powerSupply.Initialize(); //wirelessNetwork.Initialize(); } @@ -266,10 +267,7 @@ namespace SafeExamBrowser.Client.Operations { if (systemInfo.HasBattery) { - var control = uiFactory.CreatePowerSupplyControl(Location.ActionCenter); - - // TODO powerSupply.Register(control); - actionCenter.AddSystemControl(control); + actionCenter.AddSystemControl(uiFactory.CreatePowerSupplyControl(powerSupply, Location.ActionCenter)); } } @@ -277,10 +275,7 @@ namespace SafeExamBrowser.Client.Operations { if (systemInfo.HasBattery) { - var control = uiFactory.CreatePowerSupplyControl(Location.Taskbar); - - // TODO powerSupply.Register(control); - taskbar.AddSystemControl(control); + taskbar.AddSystemControl(uiFactory.CreatePowerSupplyControl(powerSupply, Location.Taskbar)); } } @@ -329,8 +324,8 @@ namespace SafeExamBrowser.Client.Operations { audio.Terminate(); keyboard.Terminate(); + powerSupply.Terminate(); // TODO - //powerSupply.Terminate(); //wirelessNetwork.Terminate(); } } diff --git a/SafeExamBrowser.SystemComponents.Contracts/BatteryChargeStatus.cs b/SafeExamBrowser.SystemComponents.Contracts/PowerSupply/BatteryChargeStatus.cs similarity index 80% rename from SafeExamBrowser.SystemComponents.Contracts/BatteryChargeStatus.cs rename to SafeExamBrowser.SystemComponents.Contracts/PowerSupply/BatteryChargeStatus.cs index 057f0e2c..3db102d4 100644 --- a/SafeExamBrowser.SystemComponents.Contracts/BatteryChargeStatus.cs +++ b/SafeExamBrowser.SystemComponents.Contracts/PowerSupply/BatteryChargeStatus.cs @@ -6,10 +6,10 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -namespace SafeExamBrowser.SystemComponents.Contracts +namespace SafeExamBrowser.SystemComponents.Contracts.PowerSupply { /// - /// Defines all possible charge statuses which can be determined by the application. + /// Defines all possible battery charge statuses which can be determined by the application. /// public enum BatteryChargeStatus { diff --git a/SafeExamBrowser.SystemComponents.Contracts/PowerSupply/Events/PowerSupplyStatusChangedEventHandler.cs b/SafeExamBrowser.SystemComponents.Contracts/PowerSupply/Events/PowerSupplyStatusChangedEventHandler.cs new file mode 100644 index 00000000..fde5570c --- /dev/null +++ b/SafeExamBrowser.SystemComponents.Contracts/PowerSupply/Events/PowerSupplyStatusChangedEventHandler.cs @@ -0,0 +1,15 @@ +/* + * 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.SystemComponents.Contracts.PowerSupply.Events +{ + /// + /// Indicates that the status of the power supply for the system has changed. + /// + public delegate void PowerSupplyStatusChangedEventHandler(IPowerSupplyStatus status); +} diff --git a/SafeExamBrowser.SystemComponents.Contracts/PowerSupply/IPowerSupply.cs b/SafeExamBrowser.SystemComponents.Contracts/PowerSupply/IPowerSupply.cs new file mode 100644 index 00000000..10e4d4f4 --- /dev/null +++ b/SafeExamBrowser.SystemComponents.Contracts/PowerSupply/IPowerSupply.cs @@ -0,0 +1,28 @@ +/* + * 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 SafeExamBrowser.SystemComponents.Contracts.PowerSupply.Events; + +namespace SafeExamBrowser.SystemComponents.Contracts.PowerSupply +{ + /// + /// Defines the functionality of the power supply. + /// + public interface IPowerSupply : ISystemComponent + { + /// + /// Fired when the status of the power supply changed. + /// + event PowerSupplyStatusChangedEventHandler StatusChanged; + + /// + /// Retrieves the current status of the power supply. + /// + IPowerSupplyStatus GetStatus(); + } +} diff --git a/SafeExamBrowser.SystemComponents.Contracts/PowerSupply/IPowerSupplyStatus.cs b/SafeExamBrowser.SystemComponents.Contracts/PowerSupply/IPowerSupplyStatus.cs new file mode 100644 index 00000000..9cd853fe --- /dev/null +++ b/SafeExamBrowser.SystemComponents.Contracts/PowerSupply/IPowerSupplyStatus.cs @@ -0,0 +1,38 @@ +/* + * 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; + +namespace SafeExamBrowser.SystemComponents.Contracts.PowerSupply +{ + /// + /// Provides data about the power supply of the system. + /// + public interface IPowerSupplyStatus + { + /// + /// Defines the current charge of the system battery: 0.0 means the battery is empty, 1.0 means it's fully charged. + /// + double BatteryCharge { get; } + + /// + /// Defines the current charge status of the system battery. + /// + BatteryChargeStatus BatteryChargeStatus { get; } + + /// + /// Defines the time remaining until the battery is empty. + /// + TimeSpan BatteryTimeRemaining { get; } + + /// + /// Indicates whether the system is connected to the power grid. + /// + bool IsOnline { get; } + } +} diff --git a/SafeExamBrowser.SystemComponents.Contracts/SafeExamBrowser.SystemComponents.Contracts.csproj b/SafeExamBrowser.SystemComponents.Contracts/SafeExamBrowser.SystemComponents.Contracts.csproj index ecf65f0c..5fac656e 100644 --- a/SafeExamBrowser.SystemComponents.Contracts/SafeExamBrowser.SystemComponents.Contracts.csproj +++ b/SafeExamBrowser.SystemComponents.Contracts/SafeExamBrowser.SystemComponents.Contracts.csproj @@ -55,7 +55,9 @@ - + + + @@ -64,6 +66,7 @@ + diff --git a/SafeExamBrowser.SystemComponents/Audio/Audio.cs b/SafeExamBrowser.SystemComponents/Audio/Audio.cs index d99713fc..56277d36 100644 --- a/SafeExamBrowser.SystemComponents/Audio/Audio.cs +++ b/SafeExamBrowser.SystemComponents/Audio/Audio.cs @@ -10,7 +10,6 @@ using System; using System.Linq; using NAudio.CoreAudioApi; using SafeExamBrowser.Configuration.Contracts.Settings; -using SafeExamBrowser.I18n.Contracts; using SafeExamBrowser.Logging.Contracts; using SafeExamBrowser.SystemComponents.Contracts.Audio; using SafeExamBrowser.SystemComponents.Contracts.Audio.Events; @@ -19,15 +18,12 @@ namespace SafeExamBrowser.SystemComponents.Audio { public class Audio : IAudio { - private readonly object @lock = new object(); - private AudioSettings settings; private MMDevice audioDevice; private string audioDeviceFullName; private string audioDeviceShortName; private float originalVolume; private ILogger logger; - private IText text; public string DeviceFullName => audioDeviceFullName ?? string.Empty; public string DeviceShortName => audioDeviceShortName ?? string.Empty; @@ -37,11 +33,10 @@ namespace SafeExamBrowser.SystemComponents.Audio public event AudioVolumeChangedEventHandler VolumeChanged; - public Audio(AudioSettings settings, ILogger logger, IText text) + public Audio(AudioSettings settings, ILogger logger) { this.settings = settings; this.logger = logger; - this.text = text; } public void Initialize() @@ -157,11 +152,8 @@ namespace SafeExamBrowser.SystemComponents.Audio private void AudioEndpointVolume_OnVolumeNotification(AudioVolumeNotificationData data) { - lock (@lock) - { - logger.Debug($"Volume is set to {data.MasterVolume * 100}%, audio device is {(data.Muted ? "muted" : "not muted")}."); - VolumeChanged?.Invoke(data.MasterVolume, data.Muted); - } + logger.Debug($"Volume is set to {data.MasterVolume * 100}%, audio device is {(data.Muted ? "muted" : "not muted")}."); + VolumeChanged?.Invoke(data.MasterVolume, data.Muted); } } } diff --git a/SafeExamBrowser.SystemComponents/Keyboard/Keyboard.cs b/SafeExamBrowser.SystemComponents/Keyboard/Keyboard.cs index 3e4ff237..3e75eddf 100644 --- a/SafeExamBrowser.SystemComponents/Keyboard/Keyboard.cs +++ b/SafeExamBrowser.SystemComponents/Keyboard/Keyboard.cs @@ -11,7 +11,6 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Windows.Input; -using SafeExamBrowser.I18n.Contracts; using SafeExamBrowser.Logging.Contracts; using SafeExamBrowser.SystemComponents.Contracts.Keyboard; using SafeExamBrowser.SystemComponents.Contracts.Keyboard.Events; @@ -23,15 +22,13 @@ namespace SafeExamBrowser.SystemComponents.Keyboard private IList layouts; private ILogger logger; private CultureInfo originalLanguage; - private IText text; public event KeyboardLayoutChangedEventHandler LayoutChanged; - public Keyboard(ILogger logger, IText text) + public Keyboard(ILogger logger) { this.layouts = new List(); this.logger = logger; - this.text = text; } public void ActivateLayout(Guid layoutId) diff --git a/SafeExamBrowser.SystemComponents/PowerSupply.cs b/SafeExamBrowser.SystemComponents/PowerSupply.cs deleted file mode 100644 index 72e04a13..00000000 --- a/SafeExamBrowser.SystemComponents/PowerSupply.cs +++ /dev/null @@ -1,146 +0,0 @@ -/* - * 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.Timers; -using SafeExamBrowser.I18n.Contracts; -using SafeExamBrowser.Logging.Contracts; -using SafeExamBrowser.SystemComponents.Contracts; -using PowerLineStatus = System.Windows.Forms.PowerLineStatus; -using SystemInformation = System.Windows.Forms.SystemInformation; - -namespace SafeExamBrowser.SystemComponents -{ - public class PowerSupply // TODO: ISystemComponent - { - private readonly object @lock = new object(); - - private bool infoShown, warningShown; - private ILogger logger; - // TODOprivate IList controls; - private IText text; - private Timer timer; - - public PowerSupply(ILogger logger, IText text) - { - // TODOthis.controls = new List(); - this.logger = logger; - this.text = text; - } - - public void Initialize() - { - const int TWO_SECONDS = 2000; - - timer = new Timer(TWO_SECONDS); - timer.Elapsed += Timer_Elapsed; - timer.AutoReset = true; - timer.Start(); - - logger.Info("Started monitoring the power supply."); - } - - // TODO - //public void Register(ISystemPowerSupplyControl control) - //{ - // lock (@lock) - // { - // controls.Add(control); - // } - - // UpdateControls(); - //} - - public void Terminate() - { - if (timer != null) - { - timer.Stop(); - logger.Info("Stopped monitoring the power supply."); - } - - // TODO - //foreach (var control in controls) - //{ - // control.Close(); - //} - } - - private void Timer_Elapsed(object sender, ElapsedEventArgs e) - { - UpdateControls(); - } - - private void UpdateControls() - { - lock (@lock) - { - var charge = SystemInformation.PowerStatus.BatteryLifePercent; - var percentage = Math.Round(charge * 100); - var status = charge <= 0.4 ? (charge <= 0.2 ? BatteryChargeStatus.Critical : BatteryChargeStatus.Low) : BatteryChargeStatus.Okay; - var online = SystemInformation.PowerStatus.PowerLineStatus == PowerLineStatus.Online; - var tooltip = string.Empty; - - if (online) - { - tooltip = text.Get(percentage == 100 ? TextKey.SystemControl_BatteryCharged : TextKey.SystemControl_BatteryCharging); - infoShown = false; - warningShown = false; - } - else - { - var hours = SystemInformation.PowerStatus.BatteryLifeRemaining / 3600; - var minutes = (SystemInformation.PowerStatus.BatteryLifeRemaining - (hours * 3600)) / 60; - - HandleBatteryStatus(status); - - tooltip = text.Get(TextKey.SystemControl_BatteryRemainingCharge); - tooltip = tooltip.Replace("%%HOURS%%", hours.ToString()); - tooltip = tooltip.Replace("%%MINUTES%%", minutes.ToString()); - } - - tooltip = tooltip.Replace("%%CHARGE%%", percentage.ToString()); - - // TODO - //foreach (var control in controls) - //{ - // control.SetBatteryCharge(charge, status); - // control.SetPowerGridConnection(online); - // control.SetInformation(tooltip); - //} - } - } - - private void HandleBatteryStatus(BatteryChargeStatus status) - { - if (status == BatteryChargeStatus.Low && !infoShown) - { - // TODO - //foreach (var control in controls) - //{ - // control.ShowLowBatteryInfo(text.Get(TextKey.SystemControl_BatteryChargeLowInfo)); - //} - - infoShown = true; - logger.Info("Informed the user about low battery charge."); - } - - if (status == BatteryChargeStatus.Critical && !warningShown) - { - // TODO - //foreach (var control in controls) - //{ - // control.ShowCriticalBatteryWarning(text.Get(TextKey.SystemControl_BatteryChargeCriticalWarning)); - //} - - warningShown = true; - logger.Warn("Warned the user about critical battery charge."); - } - } - } -} diff --git a/SafeExamBrowser.SystemComponents/PowerSupply/PowerSupply.cs b/SafeExamBrowser.SystemComponents/PowerSupply/PowerSupply.cs new file mode 100644 index 00000000..67e43749 --- /dev/null +++ b/SafeExamBrowser.SystemComponents/PowerSupply/PowerSupply.cs @@ -0,0 +1,79 @@ +/* + * 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.Timers; +using SafeExamBrowser.Logging.Contracts; +using SafeExamBrowser.SystemComponents.Contracts.PowerSupply; +using SafeExamBrowser.SystemComponents.Contracts.PowerSupply.Events; +using PowerLineStatus = System.Windows.Forms.PowerLineStatus; +using SystemInformation = System.Windows.Forms.SystemInformation; + +namespace SafeExamBrowser.SystemComponents.PowerSupply +{ + public class PowerSupply : IPowerSupply + { + private DateTime lastStatusLog; + private ILogger logger; + private Timer timer; + + public event PowerSupplyStatusChangedEventHandler StatusChanged; + + public PowerSupply(ILogger logger) + { + this.logger = logger; + } + + public IPowerSupplyStatus GetStatus() + { + var charge = SystemInformation.PowerStatus.BatteryLifePercent; + var hours = SystemInformation.PowerStatus.BatteryLifeRemaining / 3600; + var minutes = (SystemInformation.PowerStatus.BatteryLifeRemaining - (hours * 3600)) / 60; + var status = new PowerSupplyStatus(); + + status.BatteryCharge = charge; + status.BatteryChargeStatus = charge <= 0.4 ? (charge <= 0.2 ? BatteryChargeStatus.Critical : BatteryChargeStatus.Low) : BatteryChargeStatus.Okay; + status.BatteryTimeRemaining = new TimeSpan(hours, minutes, 0); + status.IsOnline = SystemInformation.PowerStatus.PowerLineStatus == PowerLineStatus.Online; + + if (lastStatusLog < DateTime.Now.AddMinutes(-1)) + { + logger.Debug($"Power grid is {(status.IsOnline ? "" : "not ")}connected, battery charge at {charge * 100}% ({status.BatteryTimeRemaining})."); + lastStatusLog = DateTime.Now; + } + + return status; + } + + public void Initialize() + { + const int TWO_SECONDS = 2000; + + timer = new Timer(TWO_SECONDS); + timer.Elapsed += Timer_Elapsed; + timer.AutoReset = true; + timer.Start(); + + logger.Info("Started monitoring the power supply."); + } + + public void Terminate() + { + if (timer != null) + { + timer.Stop(); + logger.Info("Stopped monitoring the power supply."); + } + } + + private void Timer_Elapsed(object sender, ElapsedEventArgs e) + { + StatusChanged?.Invoke(GetStatus()); + } + } +} diff --git a/SafeExamBrowser.SystemComponents/PowerSupply/PowerSupplyStatus.cs b/SafeExamBrowser.SystemComponents/PowerSupply/PowerSupplyStatus.cs new file mode 100644 index 00000000..ceefbfc0 --- /dev/null +++ b/SafeExamBrowser.SystemComponents/PowerSupply/PowerSupplyStatus.cs @@ -0,0 +1,21 @@ +/* + * 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 SafeExamBrowser.SystemComponents.Contracts.PowerSupply; + +namespace SafeExamBrowser.SystemComponents.PowerSupply +{ + internal class PowerSupplyStatus : IPowerSupplyStatus + { + public double BatteryCharge { get; set; } + public BatteryChargeStatus BatteryChargeStatus { get; set; } + public TimeSpan BatteryTimeRemaining { get; set; } + public bool IsOnline { get; set; } + } +} diff --git a/SafeExamBrowser.SystemComponents/SafeExamBrowser.SystemComponents.csproj b/SafeExamBrowser.SystemComponents/SafeExamBrowser.SystemComponents.csproj index bc698bd5..02982534 100644 --- a/SafeExamBrowser.SystemComponents/SafeExamBrowser.SystemComponents.csproj +++ b/SafeExamBrowser.SystemComponents/SafeExamBrowser.SystemComponents.csproj @@ -65,7 +65,8 @@ - + + diff --git a/SafeExamBrowser.UserInterface.Contracts/IUserInterfaceFactory.cs b/SafeExamBrowser.UserInterface.Contracts/IUserInterfaceFactory.cs index 323f8dd2..34aec4ea 100644 --- a/SafeExamBrowser.UserInterface.Contracts/IUserInterfaceFactory.cs +++ b/SafeExamBrowser.UserInterface.Contracts/IUserInterfaceFactory.cs @@ -14,6 +14,7 @@ using SafeExamBrowser.I18n.Contracts; using SafeExamBrowser.Logging.Contracts; using SafeExamBrowser.SystemComponents.Contracts.Audio; using SafeExamBrowser.SystemComponents.Contracts.Keyboard; +using SafeExamBrowser.SystemComponents.Contracts.PowerSupply; using SafeExamBrowser.UserInterface.Contracts.Browser; using SafeExamBrowser.UserInterface.Contracts.Shell; using SafeExamBrowser.UserInterface.Contracts.Windows; @@ -73,7 +74,7 @@ namespace SafeExamBrowser.UserInterface.Contracts /// /// Creates a system control displaying the power supply status of the computer. /// - ISystemPowerSupplyControl CreatePowerSupplyControl(Location location); + ISystemControl CreatePowerSupplyControl(IPowerSupply powerSupply, Location location); /// /// Creates a new runtime window which runs on its own thread. diff --git a/SafeExamBrowser.UserInterface.Contracts/SafeExamBrowser.UserInterface.Contracts.csproj b/SafeExamBrowser.UserInterface.Contracts/SafeExamBrowser.UserInterface.Contracts.csproj index 4bb990a9..ad17ad18 100644 --- a/SafeExamBrowser.UserInterface.Contracts/SafeExamBrowser.UserInterface.Contracts.csproj +++ b/SafeExamBrowser.UserInterface.Contracts/SafeExamBrowser.UserInterface.Contracts.csproj @@ -75,7 +75,6 @@ - diff --git a/SafeExamBrowser.UserInterface.Contracts/Shell/ISystemPowerSupplyControl.cs b/SafeExamBrowser.UserInterface.Contracts/Shell/ISystemPowerSupplyControl.cs deleted file mode 100644 index a5ff5699..00000000 --- a/SafeExamBrowser.UserInterface.Contracts/Shell/ISystemPowerSupplyControl.cs +++ /dev/null @@ -1,37 +0,0 @@ -/* - * 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.UserInterface.Contracts.Shell -{ - /// - /// The control of the power supply system component. - /// - public interface ISystemPowerSupplyControl : ISystemControl - { - // TODO - ///// - ///// Sets the current charge of the system battery: 0.0 means the battery is empty, 1.0 means it's fully charged. - ///// - //void SetBatteryCharge(double charge, BatteryChargeStatus status); - - /// - /// Sets the power supply status, i.e. whether the computer system is connected to the power grid or not. - /// - void SetPowerGridConnection(bool connected); - - /// - /// Warns the user that the battery charge is critical. - /// - void ShowCriticalBatteryWarning(string warning); - - /// - /// Indicates the user that the battery charge is low. - /// - void ShowLowBatteryInfo(string info); - } -} diff --git a/SafeExamBrowser.UserInterface.Desktop/Controls/ActionCenterPowerSupplyControl.xaml.cs b/SafeExamBrowser.UserInterface.Desktop/Controls/ActionCenterPowerSupplyControl.xaml.cs index 9be6af65..ded0948a 100644 --- a/SafeExamBrowser.UserInterface.Desktop/Controls/ActionCenterPowerSupplyControl.xaml.cs +++ b/SafeExamBrowser.UserInterface.Desktop/Controls/ActionCenterPowerSupplyControl.xaml.cs @@ -6,62 +6,122 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +using System; using System.Windows; using System.Windows.Controls; using System.Windows.Media; -using SafeExamBrowser.SystemComponents.Contracts; +using SafeExamBrowser.I18n.Contracts; +using SafeExamBrowser.SystemComponents.Contracts.PowerSupply; using SafeExamBrowser.UserInterface.Contracts.Shell; namespace SafeExamBrowser.UserInterface.Desktop.Controls { - public partial class ActionCenterPowerSupplyControl : UserControl, ISystemPowerSupplyControl + public partial class ActionCenterPowerSupplyControl : UserControl, ISystemControl { - private double BATTERY_CHARGE_MAX_WIDTH; + private Brush initialBrush; + private bool infoShown, warningShown; + private double maxWidth; + private IPowerSupply powerSupply; + private IText text; - public ActionCenterPowerSupplyControl() + public ActionCenterPowerSupplyControl(IPowerSupply powerSupply, IText text) { + this.powerSupply = powerSupply; + this.text = text; + InitializeComponent(); - BATTERY_CHARGE_MAX_WIDTH = BatteryCharge.Width; + InitializePowerSupplyControl(); } public void Close() { } - public void SetBatteryCharge(double charge, BatteryChargeStatus status) - { - Dispatcher.InvokeAsync(() => - { - var width = BATTERY_CHARGE_MAX_WIDTH * charge; - - width = width > BATTERY_CHARGE_MAX_WIDTH ? BATTERY_CHARGE_MAX_WIDTH : width; - width = width < 0 ? 0 : width; - - BatteryCharge.Width = width; - BatteryCharge.Fill = status == BatteryChargeStatus.Low ? Brushes.Orange : BatteryCharge.Fill; - BatteryCharge.Fill = status == BatteryChargeStatus.Critical ? Brushes.Red : BatteryCharge.Fill; - Warning.Visibility = status == BatteryChargeStatus.Critical ? Visibility.Visible : Visibility.Collapsed; - }); - } - - public void SetPowerGridConnection(bool connected) - { - Dispatcher.InvokeAsync(() => PowerPlug.Visibility = connected ? Visibility.Visible : Visibility.Collapsed); - } - public void SetInformation(string text) { Dispatcher.InvokeAsync(() => Text.Text = text); } - public void ShowCriticalBatteryWarning(string warning) + private void InitializePowerSupplyControl() { - Dispatcher.InvokeAsync(() => Button.ToolTip = warning); + initialBrush = BatteryCharge.Fill; + maxWidth = BatteryCharge.Width; + powerSupply.StatusChanged += PowerSupply_StatusChanged; + UpdateStatus(powerSupply.GetStatus()); } - public void ShowLowBatteryInfo(string info) + private void PowerSupply_StatusChanged(IPowerSupplyStatus status) { - Dispatcher.InvokeAsync(() => Button.ToolTip = info); + Dispatcher.InvokeAsync(() => UpdateStatus(status)); + } + + private void UpdateStatus(IPowerSupplyStatus status) + { + var percentage = Math.Round(status.BatteryCharge * 100); + var tooltip = string.Empty; + + RenderCharge(status.BatteryCharge, status.BatteryChargeStatus); + + if (status.IsOnline) + { + infoShown = false; + warningShown = false; + tooltip = text.Get(percentage == 100 ? TextKey.SystemControl_BatteryCharged : TextKey.SystemControl_BatteryCharging); + tooltip = tooltip.Replace("%%CHARGE%%", percentage.ToString()); + } + else + { + tooltip = text.Get(TextKey.SystemControl_BatteryRemainingCharge); + tooltip = tooltip.Replace("%%CHARGE%%", percentage.ToString()); + tooltip = tooltip.Replace("%%HOURS%%", status.BatteryTimeRemaining.Hours.ToString()); + tooltip = tooltip.Replace("%%MINUTES%%", status.BatteryTimeRemaining.Minutes.ToString()); + + HandleBatteryStatus(status.BatteryChargeStatus); + } + + if (!infoShown && !warningShown) + { + Button.ToolTip = tooltip; + } + + PowerPlug.Visibility = status.IsOnline ? Visibility.Visible : Visibility.Collapsed; + Text.Text = tooltip; + Warning.Visibility = status.BatteryChargeStatus == BatteryChargeStatus.Critical ? Visibility.Visible : Visibility.Collapsed; + } + + private void RenderCharge(double charge, BatteryChargeStatus status) + { + var width = maxWidth * charge; + + BatteryCharge.Width = width > maxWidth ? maxWidth : (width < 0 ? 0 : width); + + switch (status) + { + case BatteryChargeStatus.Critical: + BatteryCharge.Fill = Brushes.Red; + break; + case BatteryChargeStatus.Low: + BatteryCharge.Fill = Brushes.Orange; + break; + default: + BatteryCharge.Fill = initialBrush; + break; + } + } + + private void HandleBatteryStatus(BatteryChargeStatus chargeStatus) + { + if (chargeStatus == BatteryChargeStatus.Low && !infoShown) + { + Button.ToolTip = text.Get(TextKey.SystemControl_BatteryChargeLowInfo); + infoShown = true; + } + + if (chargeStatus == BatteryChargeStatus.Critical && !warningShown) + { + Button.ToolTip = text.Get(TextKey.SystemControl_BatteryChargeCriticalWarning); + warningShown = true; + } } } } diff --git a/SafeExamBrowser.UserInterface.Desktop/Controls/TaskbarPowerSupplyControl.xaml.cs b/SafeExamBrowser.UserInterface.Desktop/Controls/TaskbarPowerSupplyControl.xaml.cs index 3d8fcc4a..3792600b 100644 --- a/SafeExamBrowser.UserInterface.Desktop/Controls/TaskbarPowerSupplyControl.xaml.cs +++ b/SafeExamBrowser.UserInterface.Desktop/Controls/TaskbarPowerSupplyControl.xaml.cs @@ -6,64 +6,126 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +using System; using System.Windows; using System.Windows.Controls; using System.Windows.Media; using System.Windows.Threading; -using SafeExamBrowser.SystemComponents.Contracts; +using SafeExamBrowser.I18n.Contracts; +using SafeExamBrowser.SystemComponents.Contracts.PowerSupply; using SafeExamBrowser.UserInterface.Contracts.Shell; namespace SafeExamBrowser.UserInterface.Desktop.Controls { - public partial class TaskbarPowerSupplyControl : UserControl, ISystemPowerSupplyControl + public partial class TaskbarPowerSupplyControl : UserControl, ISystemControl { - private double BATTERY_CHARGE_MAX_WIDTH; + private Brush initialBrush; + private bool infoShown, warningShown; + private double maxWidth; + private IPowerSupply powerSupply; + private IText text; - public TaskbarPowerSupplyControl() + public TaskbarPowerSupplyControl(IPowerSupply powerSupply, IText text) { + this.powerSupply = powerSupply; + this.text = text; + InitializeComponent(); - BATTERY_CHARGE_MAX_WIDTH = BatteryCharge.Width; + InitializePowerSupplyControl(); } public void Close() { - Popup.IsOpen = false; - } - - public void SetBatteryCharge(double charge, BatteryChargeStatus status) - { - Dispatcher.InvokeAsync(() => - { - var width = BATTERY_CHARGE_MAX_WIDTH * charge; - - width = width > BATTERY_CHARGE_MAX_WIDTH ? BATTERY_CHARGE_MAX_WIDTH : width; - width = width < 0 ? 0 : width; - - BatteryCharge.Width = width; - BatteryCharge.Fill = status == BatteryChargeStatus.Low ? Brushes.Orange : BatteryCharge.Fill; - BatteryCharge.Fill = status == BatteryChargeStatus.Critical ? Brushes.Red : BatteryCharge.Fill; - Warning.Visibility = status == BatteryChargeStatus.Critical ? Visibility.Visible : Visibility.Collapsed; - }); - } - - public void SetPowerGridConnection(bool connected) - { - Dispatcher.InvokeAsync(() => PowerPlug.Visibility = connected ? Visibility.Visible : Visibility.Collapsed); + Dispatcher.InvokeAsync(ClosePopup); } public void SetInformation(string text) { - Dispatcher.InvokeAsync(() => Button.ToolTip = text); + Dispatcher.InvokeAsync(() => PopupText.Text = text); } - public void ShowCriticalBatteryWarning(string warning) + private void InitializePowerSupplyControl() { - Dispatcher.InvokeAsync(() => ShowPopup(warning)); + initialBrush = BatteryCharge.Fill; + maxWidth = BatteryCharge.Width; + powerSupply.StatusChanged += PowerSupply_StatusChanged; + UpdateStatus(powerSupply.GetStatus()); } - public void ShowLowBatteryInfo(string info) + private void Button_Click(object sender, RoutedEventArgs e) { - Dispatcher.InvokeAsync(() => ShowPopup(info)); + ClosePopup(); + } + + private void PowerSupply_StatusChanged(IPowerSupplyStatus status) + { + Dispatcher.InvokeAsync(() => UpdateStatus(status)); + } + + private void UpdateStatus(IPowerSupplyStatus status) + { + var percentage = Math.Round(status.BatteryCharge * 100); + var tooltip = string.Empty; + + RenderCharge(status.BatteryCharge, status.BatteryChargeStatus); + + if (status.IsOnline) + { + infoShown = false; + warningShown = false; + tooltip = text.Get(percentage == 100 ? TextKey.SystemControl_BatteryCharged : TextKey.SystemControl_BatteryCharging); + tooltip = tooltip.Replace("%%CHARGE%%", percentage.ToString()); + + ClosePopup(); + } + else + { + tooltip = text.Get(TextKey.SystemControl_BatteryRemainingCharge); + tooltip = tooltip.Replace("%%CHARGE%%", percentage.ToString()); + tooltip = tooltip.Replace("%%HOURS%%", status.BatteryTimeRemaining.Hours.ToString()); + tooltip = tooltip.Replace("%%MINUTES%%", status.BatteryTimeRemaining.Minutes.ToString()); + + HandleBatteryStatus(status.BatteryChargeStatus); + } + + Button.ToolTip = tooltip; + PowerPlug.Visibility = status.IsOnline ? Visibility.Visible : Visibility.Collapsed; + Warning.Visibility = status.BatteryChargeStatus == BatteryChargeStatus.Critical ? Visibility.Visible : Visibility.Collapsed; + } + + private void RenderCharge(double charge, BatteryChargeStatus status) + { + var width = maxWidth * charge; + + BatteryCharge.Width = width > maxWidth ? maxWidth : (width < 0 ? 0 : width); + + switch (status) + { + case BatteryChargeStatus.Critical: + BatteryCharge.Fill = Brushes.Red; + break; + case BatteryChargeStatus.Low: + BatteryCharge.Fill = Brushes.Orange; + break; + default: + BatteryCharge.Fill = initialBrush; + break; + } + } + + private void HandleBatteryStatus(BatteryChargeStatus chargeStatus) + { + if (chargeStatus == BatteryChargeStatus.Low && !infoShown) + { + ShowPopup(text.Get(TextKey.SystemControl_BatteryChargeLowInfo)); + infoShown = true; + } + + if (chargeStatus == BatteryChargeStatus.Critical && !warningShown) + { + ShowPopup(text.Get(TextKey.SystemControl_BatteryChargeCriticalWarning)); + warningShown = true; + } } private void ShowPopup(string text) @@ -73,7 +135,7 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls Background = Brushes.LightGray; } - private void Button_Click(object sender, RoutedEventArgs e) + private void ClosePopup() { Popup.IsOpen = false; Background = Brushes.Transparent; diff --git a/SafeExamBrowser.UserInterface.Desktop/UserInterfaceFactory.cs b/SafeExamBrowser.UserInterface.Desktop/UserInterfaceFactory.cs index e501cde2..72e320a8 100644 --- a/SafeExamBrowser.UserInterface.Desktop/UserInterfaceFactory.cs +++ b/SafeExamBrowser.UserInterface.Desktop/UserInterfaceFactory.cs @@ -18,6 +18,7 @@ using SafeExamBrowser.I18n.Contracts; using SafeExamBrowser.Logging.Contracts; using SafeExamBrowser.SystemComponents.Contracts.Audio; using SafeExamBrowser.SystemComponents.Contracts.Keyboard; +using SafeExamBrowser.SystemComponents.Contracts.PowerSupply; using SafeExamBrowser.UserInterface.Contracts; using SafeExamBrowser.UserInterface.Contracts.Browser; using SafeExamBrowser.UserInterface.Contracts.Shell; @@ -129,15 +130,15 @@ namespace SafeExamBrowser.UserInterface.Desktop return Application.Current.Dispatcher.Invoke(() => new PasswordDialog(text.Get(message), text.Get(title), text)); } - public ISystemPowerSupplyControl CreatePowerSupplyControl(Location location) + public ISystemControl CreatePowerSupplyControl(IPowerSupply powerSupply, Location location) { if (location == Location.ActionCenter) { - return new ActionCenterPowerSupplyControl(); + return new ActionCenterPowerSupplyControl(powerSupply, text); } else { - return new TaskbarPowerSupplyControl(); + return new TaskbarPowerSupplyControl(powerSupply, text); } } diff --git a/SafeExamBrowser.UserInterface.Mobile/Controls/ActionCenterPowerSupplyControl.xaml.cs b/SafeExamBrowser.UserInterface.Mobile/Controls/ActionCenterPowerSupplyControl.xaml.cs index acdb838b..69792b7f 100644 --- a/SafeExamBrowser.UserInterface.Mobile/Controls/ActionCenterPowerSupplyControl.xaml.cs +++ b/SafeExamBrowser.UserInterface.Mobile/Controls/ActionCenterPowerSupplyControl.xaml.cs @@ -6,62 +6,122 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +using System; using System.Windows; using System.Windows.Controls; using System.Windows.Media; -using SafeExamBrowser.SystemComponents.Contracts; +using SafeExamBrowser.I18n.Contracts; +using SafeExamBrowser.SystemComponents.Contracts.PowerSupply; using SafeExamBrowser.UserInterface.Contracts.Shell; namespace SafeExamBrowser.UserInterface.Mobile.Controls { - public partial class ActionCenterPowerSupplyControl : UserControl, ISystemPowerSupplyControl + public partial class ActionCenterPowerSupplyControl : UserControl, ISystemControl { - private double BATTERY_CHARGE_MAX_WIDTH; + private Brush initialBrush; + private bool infoShown, warningShown; + private double maxWidth; + private IPowerSupply powerSupply; + private IText text; - public ActionCenterPowerSupplyControl() + public ActionCenterPowerSupplyControl(IPowerSupply powerSupply, IText text) { + this.powerSupply = powerSupply; + this.text = text; + InitializeComponent(); - BATTERY_CHARGE_MAX_WIDTH = BatteryCharge.Width; + InitializePowerSupplyControl(); } public void Close() { } - public void SetBatteryCharge(double charge, BatteryChargeStatus status) - { - Dispatcher.InvokeAsync(() => - { - var width = BATTERY_CHARGE_MAX_WIDTH * charge; - - width = width > BATTERY_CHARGE_MAX_WIDTH ? BATTERY_CHARGE_MAX_WIDTH : width; - width = width < 0 ? 0 : width; - - BatteryCharge.Width = width; - BatteryCharge.Fill = status == BatteryChargeStatus.Low ? Brushes.Orange : BatteryCharge.Fill; - BatteryCharge.Fill = status == BatteryChargeStatus.Critical ? Brushes.Red : BatteryCharge.Fill; - Warning.Visibility = status == BatteryChargeStatus.Critical ? Visibility.Visible : Visibility.Collapsed; - }); - } - - public void SetPowerGridConnection(bool connected) - { - Dispatcher.InvokeAsync(() => PowerPlug.Visibility = connected ? Visibility.Visible : Visibility.Collapsed); - } - public void SetInformation(string text) { Dispatcher.InvokeAsync(() => Text.Text = text); } - public void ShowCriticalBatteryWarning(string warning) + private void InitializePowerSupplyControl() { - Dispatcher.InvokeAsync(() => Button.ToolTip = warning); + initialBrush = BatteryCharge.Fill; + maxWidth = BatteryCharge.Width; + powerSupply.StatusChanged += PowerSupply_StatusChanged; + UpdateStatus(powerSupply.GetStatus()); } - public void ShowLowBatteryInfo(string info) + private void PowerSupply_StatusChanged(IPowerSupplyStatus status) { - Dispatcher.InvokeAsync(() => Button.ToolTip = info); + Dispatcher.InvokeAsync(() => UpdateStatus(status)); + } + + private void UpdateStatus(IPowerSupplyStatus status) + { + var percentage = Math.Round(status.BatteryCharge * 100); + var tooltip = string.Empty; + + RenderCharge(status.BatteryCharge, status.BatteryChargeStatus); + + if (status.IsOnline) + { + infoShown = false; + warningShown = false; + tooltip = text.Get(percentage == 100 ? TextKey.SystemControl_BatteryCharged : TextKey.SystemControl_BatteryCharging); + tooltip = tooltip.Replace("%%CHARGE%%", percentage.ToString()); + } + else + { + tooltip = text.Get(TextKey.SystemControl_BatteryRemainingCharge); + tooltip = tooltip.Replace("%%CHARGE%%", percentage.ToString()); + tooltip = tooltip.Replace("%%HOURS%%", status.BatteryTimeRemaining.Hours.ToString()); + tooltip = tooltip.Replace("%%MINUTES%%", status.BatteryTimeRemaining.Minutes.ToString()); + + HandleBatteryStatus(status.BatteryChargeStatus); + } + + if (!infoShown && !warningShown) + { + Button.ToolTip = tooltip; + } + + PowerPlug.Visibility = status.IsOnline ? Visibility.Visible : Visibility.Collapsed; + Text.Text = tooltip; + Warning.Visibility = status.BatteryChargeStatus == BatteryChargeStatus.Critical ? Visibility.Visible : Visibility.Collapsed; + } + + private void RenderCharge(double charge, BatteryChargeStatus status) + { + var width = maxWidth * charge; + + BatteryCharge.Width = width > maxWidth ? maxWidth : (width < 0 ? 0 : width); + + switch (status) + { + case BatteryChargeStatus.Critical: + BatteryCharge.Fill = Brushes.Red; + break; + case BatteryChargeStatus.Low: + BatteryCharge.Fill = Brushes.Orange; + break; + default: + BatteryCharge.Fill = initialBrush; + break; + } + } + + private void HandleBatteryStatus(BatteryChargeStatus chargeStatus) + { + if (chargeStatus == BatteryChargeStatus.Low && !infoShown) + { + Button.ToolTip = text.Get(TextKey.SystemControl_BatteryChargeLowInfo); + infoShown = true; + } + + if (chargeStatus == BatteryChargeStatus.Critical && !warningShown) + { + Button.ToolTip = text.Get(TextKey.SystemControl_BatteryChargeCriticalWarning); + warningShown = true; + } } } } diff --git a/SafeExamBrowser.UserInterface.Mobile/Controls/TaskbarPowerSupplyControl.xaml.cs b/SafeExamBrowser.UserInterface.Mobile/Controls/TaskbarPowerSupplyControl.xaml.cs index eb29eb77..819d076e 100644 --- a/SafeExamBrowser.UserInterface.Mobile/Controls/TaskbarPowerSupplyControl.xaml.cs +++ b/SafeExamBrowser.UserInterface.Mobile/Controls/TaskbarPowerSupplyControl.xaml.cs @@ -6,64 +6,126 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +using System; using System.Windows; using System.Windows.Controls; using System.Windows.Media; using System.Windows.Threading; -using SafeExamBrowser.SystemComponents.Contracts; +using SafeExamBrowser.I18n.Contracts; +using SafeExamBrowser.SystemComponents.Contracts.PowerSupply; using SafeExamBrowser.UserInterface.Contracts.Shell; namespace SafeExamBrowser.UserInterface.Mobile.Controls { - public partial class TaskbarPowerSupplyControl : UserControl, ISystemPowerSupplyControl + public partial class TaskbarPowerSupplyControl : UserControl, ISystemControl { - private double BATTERY_CHARGE_MAX_WIDTH; + private Brush initialBrush; + private bool infoShown, warningShown; + private double maxWidth; + private IPowerSupply powerSupply; + private IText text; - public TaskbarPowerSupplyControl() + public TaskbarPowerSupplyControl(IPowerSupply powerSupply, IText text) { + this.powerSupply = powerSupply; + this.text = text; + InitializeComponent(); - BATTERY_CHARGE_MAX_WIDTH = BatteryCharge.Width; + InitializePowerSupplyControl(); } public void Close() { - Popup.IsOpen = false; - } - - public void SetBatteryCharge(double charge, BatteryChargeStatus status) - { - Dispatcher.InvokeAsync(() => - { - var width = BATTERY_CHARGE_MAX_WIDTH * charge; - - width = width > BATTERY_CHARGE_MAX_WIDTH ? BATTERY_CHARGE_MAX_WIDTH : width; - width = width < 0 ? 0 : width; - - BatteryCharge.Width = width; - BatteryCharge.Fill = status == BatteryChargeStatus.Low ? Brushes.Orange : BatteryCharge.Fill; - BatteryCharge.Fill = status == BatteryChargeStatus.Critical ? Brushes.Red : BatteryCharge.Fill; - Warning.Visibility = status == BatteryChargeStatus.Critical ? Visibility.Visible : Visibility.Collapsed; - }); - } - - public void SetPowerGridConnection(bool connected) - { - Dispatcher.InvokeAsync(() => PowerPlug.Visibility = connected ? Visibility.Visible : Visibility.Collapsed); + Dispatcher.InvokeAsync(ClosePopup); } public void SetInformation(string text) { - Dispatcher.InvokeAsync(() => Button.ToolTip = text); + Dispatcher.InvokeAsync(() => PopupText.Text = text); } - public void ShowCriticalBatteryWarning(string warning) + private void InitializePowerSupplyControl() { - Dispatcher.InvokeAsync(() => ShowPopup(warning)); + initialBrush = BatteryCharge.Fill; + maxWidth = BatteryCharge.Width; + powerSupply.StatusChanged += PowerSupply_StatusChanged; + UpdateStatus(powerSupply.GetStatus()); } - public void ShowLowBatteryInfo(string info) + private void Button_Click(object sender, RoutedEventArgs e) { - Dispatcher.InvokeAsync(() => ShowPopup(info)); + ClosePopup(); + } + + private void PowerSupply_StatusChanged(IPowerSupplyStatus status) + { + Dispatcher.InvokeAsync(() => UpdateStatus(status)); + } + + private void UpdateStatus(IPowerSupplyStatus status) + { + var percentage = Math.Round(status.BatteryCharge * 100); + var tooltip = string.Empty; + + RenderCharge(status.BatteryCharge, status.BatteryChargeStatus); + + if (status.IsOnline) + { + infoShown = false; + warningShown = false; + tooltip = text.Get(percentage == 100 ? TextKey.SystemControl_BatteryCharged : TextKey.SystemControl_BatteryCharging); + tooltip = tooltip.Replace("%%CHARGE%%", percentage.ToString()); + + ClosePopup(); + } + else + { + tooltip = text.Get(TextKey.SystemControl_BatteryRemainingCharge); + tooltip = tooltip.Replace("%%CHARGE%%", percentage.ToString()); + tooltip = tooltip.Replace("%%HOURS%%", status.BatteryTimeRemaining.Hours.ToString()); + tooltip = tooltip.Replace("%%MINUTES%%", status.BatteryTimeRemaining.Minutes.ToString()); + + HandleBatteryStatus(status.BatteryChargeStatus); + } + + Button.ToolTip = tooltip; + PowerPlug.Visibility = status.IsOnline ? Visibility.Visible : Visibility.Collapsed; + Warning.Visibility = status.BatteryChargeStatus == BatteryChargeStatus.Critical ? Visibility.Visible : Visibility.Collapsed; + } + + private void RenderCharge(double charge, BatteryChargeStatus status) + { + var width = maxWidth * charge; + + BatteryCharge.Width = width > maxWidth ? maxWidth : (width < 0 ? 0 : width); + + switch (status) + { + case BatteryChargeStatus.Critical: + BatteryCharge.Fill = Brushes.Red; + break; + case BatteryChargeStatus.Low: + BatteryCharge.Fill = Brushes.Orange; + break; + default: + BatteryCharge.Fill = initialBrush; + break; + } + } + + private void HandleBatteryStatus(BatteryChargeStatus chargeStatus) + { + if (chargeStatus == BatteryChargeStatus.Low && !infoShown) + { + ShowPopup(text.Get(TextKey.SystemControl_BatteryChargeLowInfo)); + infoShown = true; + } + + if (chargeStatus == BatteryChargeStatus.Critical && !warningShown) + { + ShowPopup(text.Get(TextKey.SystemControl_BatteryChargeCriticalWarning)); + warningShown = true; + } } private void ShowPopup(string text) @@ -73,7 +135,7 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls Background = Brushes.LightGray; } - private void Button_Click(object sender, RoutedEventArgs e) + private void ClosePopup() { Popup.IsOpen = false; Background = Brushes.Transparent; diff --git a/SafeExamBrowser.UserInterface.Mobile/UserInterfaceFactory.cs b/SafeExamBrowser.UserInterface.Mobile/UserInterfaceFactory.cs index 30acca80..89b9aceb 100644 --- a/SafeExamBrowser.UserInterface.Mobile/UserInterfaceFactory.cs +++ b/SafeExamBrowser.UserInterface.Mobile/UserInterfaceFactory.cs @@ -18,6 +18,7 @@ using SafeExamBrowser.I18n.Contracts; using SafeExamBrowser.Logging.Contracts; using SafeExamBrowser.SystemComponents.Contracts.Audio; using SafeExamBrowser.SystemComponents.Contracts.Keyboard; +using SafeExamBrowser.SystemComponents.Contracts.PowerSupply; using SafeExamBrowser.UserInterface.Contracts; using SafeExamBrowser.UserInterface.Contracts.Browser; using SafeExamBrowser.UserInterface.Contracts.Shell; @@ -129,15 +130,15 @@ namespace SafeExamBrowser.UserInterface.Mobile return Application.Current.Dispatcher.Invoke(() => new PasswordDialog(text.Get(message), text.Get(title), text)); } - public ISystemPowerSupplyControl CreatePowerSupplyControl(Location location) + public ISystemControl CreatePowerSupplyControl(IPowerSupply powerSupply, Location location) { if (location == Location.ActionCenter) { - return new ActionCenterPowerSupplyControl(); + return new ActionCenterPowerSupplyControl(powerSupply, text); } else { - return new TaskbarPowerSupplyControl(); + return new TaskbarPowerSupplyControl(powerSupply, text); } }