diff --git a/SafeExamBrowser.Contracts/I18n/TextKey.cs b/SafeExamBrowser.Contracts/I18n/TextKey.cs
index 00e9bcd0..ab03bb45 100644
--- a/SafeExamBrowser.Contracts/I18n/TextKey.cs
+++ b/SafeExamBrowser.Contracts/I18n/TextKey.cs
@@ -43,6 +43,11 @@ namespace SafeExamBrowser.Contracts.I18n
SplashScreen_TerminateBrowser,
SplashScreen_WaitExplorerStartup,
SplashScreen_WaitExplorerTermination,
+ SystemControl_BatteryCharged,
+ SystemControl_BatteryCharging,
+ SystemControl_BatteryChargeCriticalWarning,
+ SystemControl_BatteryChargeLowInfo,
+ SystemControl_BatteryRemainingCharge,
Version
}
}
diff --git a/SafeExamBrowser.Contracts/SafeExamBrowser.Contracts.csproj b/SafeExamBrowser.Contracts/SafeExamBrowser.Contracts.csproj
index d242d5cb..06f174d7 100644
--- a/SafeExamBrowser.Contracts/SafeExamBrowser.Contracts.csproj
+++ b/SafeExamBrowser.Contracts/SafeExamBrowser.Contracts.csproj
@@ -86,6 +86,7 @@
+
diff --git a/SafeExamBrowser.Contracts/SystemComponents/BatteryChargeStatus.cs b/SafeExamBrowser.Contracts/SystemComponents/BatteryChargeStatus.cs
new file mode 100644
index 00000000..2e1b24f9
--- /dev/null
+++ b/SafeExamBrowser.Contracts/SystemComponents/BatteryChargeStatus.cs
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2017 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.SystemComponents
+{
+ public enum BatteryChargeStatus
+ {
+ Undefined = 0,
+
+ ///
+ /// The battery charge is critical, i.e. below 20%.
+ ///
+ Critical,
+
+ ///
+ /// The battery charge is low, i.e. below 35%.
+ ///
+ Low,
+
+ ///
+ /// The battery charge is okay, i.e. above 35%.
+ ///
+ Okay
+ }
+}
diff --git a/SafeExamBrowser.Contracts/SystemComponents/ISystemComponent.cs b/SafeExamBrowser.Contracts/SystemComponents/ISystemComponent.cs
index 1ad5ed72..a8d27dfc 100644
--- a/SafeExamBrowser.Contracts/SystemComponents/ISystemComponent.cs
+++ b/SafeExamBrowser.Contracts/SystemComponents/ISystemComponent.cs
@@ -13,17 +13,12 @@ namespace SafeExamBrowser.Contracts.SystemComponents
public interface ISystemComponent where TControl : ISystemControl
{
///
- /// Initializes the resources used by the component and starts its operations, if applicable.
+ /// Initializes the resources and operations of the component and registers its taskbar control.
///
- void Initialize();
+ void Initialize(TControl control);
///
- /// Registers the taskbar control for the system component.
- ///
- void RegisterControl(TControl control);
-
- ///
- /// Instructs the component to stop any running operations and release all used resources.
+ /// Instructs the component to stop any running operations and releases all used resources.
///
void Terminate();
}
diff --git a/SafeExamBrowser.Contracts/UserInterface/Taskbar/ISystemControl.cs b/SafeExamBrowser.Contracts/UserInterface/Taskbar/ISystemControl.cs
index e1ff5b05..467ea4f2 100644
--- a/SafeExamBrowser.Contracts/UserInterface/Taskbar/ISystemControl.cs
+++ b/SafeExamBrowser.Contracts/UserInterface/Taskbar/ISystemControl.cs
@@ -10,6 +10,11 @@ namespace SafeExamBrowser.Contracts.UserInterface.Taskbar
{
public interface ISystemControl
{
+ ///
+ /// Closes any pop-up windows associated with this control.
+ ///
+ void Close();
+
///
/// Sets the tooltip text of the system control.
///
diff --git a/SafeExamBrowser.Contracts/UserInterface/Taskbar/ISystemPowerSupplyControl.cs b/SafeExamBrowser.Contracts/UserInterface/Taskbar/ISystemPowerSupplyControl.cs
index 53c3d3cc..a02a357b 100644
--- a/SafeExamBrowser.Contracts/UserInterface/Taskbar/ISystemPowerSupplyControl.cs
+++ b/SafeExamBrowser.Contracts/UserInterface/Taskbar/ISystemPowerSupplyControl.cs
@@ -6,19 +6,30 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
+using SafeExamBrowser.Contracts.SystemComponents;
+
namespace SafeExamBrowser.Contracts.UserInterface.Taskbar
{
public interface ISystemPowerSupplyControl : ISystemControl
{
///
- /// Sets the current charge of the system battery, if available. 0.0 means the battery is empty, 1.0 means it's
- /// fully charged. Pass null to indicate that the computer system has no battery.
+ /// 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? percentage);
+ 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.Core/Behaviour/Operations/TaskbarOperation.cs b/SafeExamBrowser.Core/Behaviour/Operations/TaskbarOperation.cs
index d10e2c27..b72b8fcd 100644
--- a/SafeExamBrowser.Core/Behaviour/Operations/TaskbarOperation.cs
+++ b/SafeExamBrowser.Core/Behaviour/Operations/TaskbarOperation.cs
@@ -60,7 +60,11 @@ namespace SafeExamBrowser.Core.Behaviour.Operations
}
CreateAboutNotification();
- CreatePowerSupplyComponent();
+
+ if (systemInfo.HasBattery)
+ {
+ CreatePowerSupplyComponent();
+ }
}
public void Revert()
@@ -68,7 +72,10 @@ namespace SafeExamBrowser.Core.Behaviour.Operations
logController?.Terminate();
aboutController?.Terminate();
- powerSupply.Terminate();
+ if (systemInfo.HasBattery)
+ {
+ powerSupply.Terminate();
+ }
}
private void CreateLogNotification()
@@ -97,9 +104,7 @@ namespace SafeExamBrowser.Core.Behaviour.Operations
{
var control = uiFactory.CreatePowerSupplyControl();
- powerSupply.RegisterControl(control);
- powerSupply.Initialize();
-
+ powerSupply.Initialize(control);
taskbar.AddSystemControl(control);
}
}
diff --git a/SafeExamBrowser.Core/I18n/Text.xml b/SafeExamBrowser.Core/I18n/Text.xml
index 73fd5e5a..fa38af67 100644
--- a/SafeExamBrowser.Core/I18n/Text.xml
+++ b/SafeExamBrowser.Core/I18n/Text.xml
@@ -1,32 +1,37 @@
- Open Console
- Application Log
- An unexpected error occurred during the shutdown procedure! Please consult the application log for more information...
- Shutdown Error
- An unexpected error occurred during the startup procedure! Please consult the application log for more information...
- Startup Error
- About Safe Exam Browser
- Application Log
- Emptying clipboard
- Initializing browser
- Initializing process monitoring
- Initializing taskbar
- Initializing window monitoring
- Initializing working area
- Restoring working area
- Initiating shutdown procedure
- Starting event handling
- Starting keyboard interception
- Starting mouse interception
- Initiating startup procedure
- Stopping event handling
- Stopping keyboard interception
- Stopping mouse interception
- Stopping process monitoring
- Stopping window monitoring
- Terminating browser
- Waiting for Windows explorer to start up
- Waiting for Windows explorer to shut down
- Version
+ Open Console
+ Application Log
+ An unexpected error occurred during the shutdown procedure! Please consult the application log for more information...
+ Shutdown Error
+ An unexpected error occurred during the startup procedure! Please consult the application log for more information...
+ Startup Error
+ About Safe Exam Browser
+ Application Log
+ Emptying clipboard
+ Initializing browser
+ Initializing process monitoring
+ Initializing taskbar
+ Initializing window monitoring
+ Initializing working area
+ Restoring working area
+ Initiating shutdown procedure
+ Starting event handling
+ Starting keyboard interception
+ Starting mouse interception
+ Initiating startup procedure
+ Stopping event handling
+ Stopping keyboard interception
+ Stopping mouse interception
+ Stopping process monitoring
+ Stopping window monitoring
+ Terminating browser
+ Waiting for Windows explorer to start up
+ Waiting for Windows explorer to shut down
+ Charging... (%%CHARGE%%%)
+ Fully charged (%%CHARGE%%%)
+ The battery charge is critically low. Please connect your computer to a power supply!
+ The battery charge is getting low. Consider connecting your computer to a power supply in time...
+ %%HOURS%%h %%MINUTES%%min remaining (%%CHARGE%%%)
+ Version
\ No newline at end of file
diff --git a/SafeExamBrowser.SystemComponents/PowerSupply.cs b/SafeExamBrowser.SystemComponents/PowerSupply.cs
index 9641075f..9efe70cc 100644
--- a/SafeExamBrowser.SystemComponents/PowerSupply.cs
+++ b/SafeExamBrowser.SystemComponents/PowerSupply.cs
@@ -6,35 +6,102 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
+using System;
+using System.Timers;
+using SafeExamBrowser.Contracts.I18n;
using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.SystemComponents;
using SafeExamBrowser.Contracts.UserInterface.Taskbar;
+using PowerLineStatus = System.Windows.Forms.PowerLineStatus;
+using SystemInformation = System.Windows.Forms.SystemInformation;
namespace SafeExamBrowser.SystemComponents
{
public class PowerSupply : ISystemComponent
{
+ private const int TWO_SECONDS = 2000;
+
+ private bool infoShown, warningShown;
private ILogger logger;
private ISystemPowerSupplyControl control;
+ private IText text;
+ private Timer timer;
- public PowerSupply(ILogger logger)
+ public PowerSupply(ILogger logger, IText text)
{
this.logger = logger;
+ this.text = text;
}
- public void Initialize()
- {
-
- }
-
- public void RegisterControl(ISystemPowerSupplyControl control)
+ public void Initialize(ISystemPowerSupplyControl control)
{
this.control = control;
+
+ UpdateControl();
+
+ timer = new Timer(TWO_SECONDS);
+ timer.Elapsed += Timer_Elapsed;
+ timer.AutoReset = true;
+ timer.Start();
+
+ logger.Info("Started monitoring the power supply.");
+ }
+
+ private void Timer_Elapsed(object sender, ElapsedEventArgs e)
+ {
+ UpdateControl();
}
public void Terminate()
{
-
+ timer?.Stop();
+ control?.Close();
+ logger.Info("Stopped monitoring the power supply.");
+ }
+
+ private void UpdateControl()
+ {
+ var charge = SystemInformation.PowerStatus.BatteryLifePercent;
+ var percentage = Math.Round(charge * 100);
+ var status = charge <= 0.35 ? (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;
+
+ if (status == BatteryChargeStatus.Low && !infoShown)
+ {
+ control.ShowLowBatteryInfo(text.Get(TextKey.SystemControl_BatteryChargeLowInfo));
+ infoShown = true;
+ logger.Info("Informed the user about low battery charge.");
+ }
+
+ if (status == BatteryChargeStatus.Critical && !warningShown)
+ {
+ control.ShowCriticalBatteryWarning(text.Get(TextKey.SystemControl_BatteryChargeCriticalWarning));
+ warningShown = true;
+ logger.Warn("Warned the user about critical battery charge.");
+ }
+
+ tooltip = text.Get(TextKey.SystemControl_BatteryRemainingCharge);
+ tooltip = tooltip.Replace("%%HOURS%%", hours.ToString());
+ tooltip = tooltip.Replace("%%MINUTES%%", minutes.ToString());
+ }
+
+ tooltip = tooltip.Replace("%%CHARGE%%", percentage.ToString());
+
+ control.SetBatteryCharge(charge, status);
+ control.SetPowerGridConnection(online);
+ control.SetTooltip(tooltip);
}
}
}
diff --git a/SafeExamBrowser.SystemComponents/SafeExamBrowser.SystemComponents.csproj b/SafeExamBrowser.SystemComponents/SafeExamBrowser.SystemComponents.csproj
index cfd6c74e..80d5eb7f 100644
--- a/SafeExamBrowser.SystemComponents/SafeExamBrowser.SystemComponents.csproj
+++ b/SafeExamBrowser.SystemComponents/SafeExamBrowser.SystemComponents.csproj
@@ -50,6 +50,7 @@
+
diff --git a/SafeExamBrowser.UserInterface/Controls/ApplicationButton.xaml b/SafeExamBrowser.UserInterface/Controls/ApplicationButton.xaml
index c81c6a65..50ef9ceb 100644
--- a/SafeExamBrowser.UserInterface/Controls/ApplicationButton.xaml
+++ b/SafeExamBrowser.UserInterface/Controls/ApplicationButton.xaml
@@ -9,7 +9,7 @@
-
+
diff --git a/SafeExamBrowser.UserInterface/Controls/NotificationButton.xaml b/SafeExamBrowser.UserInterface/Controls/NotificationButton.xaml
index 5b826044..a8deeb95 100644
--- a/SafeExamBrowser.UserInterface/Controls/NotificationButton.xaml
+++ b/SafeExamBrowser.UserInterface/Controls/NotificationButton.xaml
@@ -8,7 +8,7 @@
-
+
diff --git a/SafeExamBrowser.UserInterface/Controls/PowerSupplyControl.xaml b/SafeExamBrowser.UserInterface/Controls/PowerSupplyControl.xaml
index dbbd7983..fca76ce7 100644
--- a/SafeExamBrowser.UserInterface/Controls/PowerSupplyControl.xaml
+++ b/SafeExamBrowser.UserInterface/Controls/PowerSupplyControl.xaml
@@ -8,19 +8,46 @@
-
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/SafeExamBrowser.UserInterface/Controls/PowerSupplyControl.xaml.cs b/SafeExamBrowser.UserInterface/Controls/PowerSupplyControl.xaml.cs
index 13b8fd6a..2c19e22b 100644
--- a/SafeExamBrowser.UserInterface/Controls/PowerSupplyControl.xaml.cs
+++ b/SafeExamBrowser.UserInterface/Controls/PowerSupplyControl.xaml.cs
@@ -6,42 +6,70 @@
* 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.Contracts.SystemComponents;
using SafeExamBrowser.Contracts.UserInterface.Taskbar;
namespace SafeExamBrowser.UserInterface.Controls
{
public partial class PowerSupplyControl : UserControl, ISystemPowerSupplyControl
{
+ private double BATTERY_CHARGE_MAX_WIDTH;
+
public PowerSupplyControl()
{
InitializeComponent();
- InitializePowerSupplyControl();
+ BATTERY_CHARGE_MAX_WIDTH = BatteryCharge.Width;
}
- public void SetBatteryCharge(double? percentage)
+ public void Close()
{
- throw new NotImplementedException();
+ Popup.IsOpen = false;
+ }
+
+ public void SetBatteryCharge(double charge, BatteryChargeStatus status)
+ {
+ Dispatcher.Invoke(() =>
+ {
+ BatteryCharge.Width = BATTERY_CHARGE_MAX_WIDTH * charge;
+ BatteryCharge.Fill = status == BatteryChargeStatus.Low ? (status == BatteryChargeStatus.Critical ? Brushes.Red : Brushes.Orange) : Brushes.Green;
+ Warning.Visibility = status == BatteryChargeStatus.Critical ? Visibility.Visible : Visibility.Collapsed;
+ });
}
public void SetPowerGridConnection(bool connected)
{
- throw new NotImplementedException();
+ Dispatcher.Invoke(() => PowerPlug.Visibility = connected ? Visibility.Visible : Visibility.Collapsed);
}
public void SetTooltip(string text)
{
- throw new NotImplementedException();
+ Dispatcher.Invoke(() => Button.ToolTip = text);
}
- private void InitializePowerSupplyControl()
+ public void ShowCriticalBatteryWarning(string warning)
{
- Button.Resources.MergedDictionaries.Add(new ResourceDictionary
- {
- Source = (new Uri("/SafeExamBrowser.UserInterface;component/Styles/ButtonStyles.xaml", UriKind.RelativeOrAbsolute))
- });
+ Dispatcher.Invoke(() => ShowPopup(warning));
+ }
+
+ public void ShowLowBatteryInfo(string info)
+ {
+ Dispatcher.Invoke(() => ShowPopup(info));
+ }
+
+ private void ShowPopup(string text)
+ {
+ Popup.IsOpen = true;
+ PopupText.Text = text;
+ Background = (Brush) new BrushConverter().ConvertFrom("#2AFFFFFF");
+ }
+
+ private void Button_Click(object sender, RoutedEventArgs e)
+ {
+ Popup.IsOpen = false;
+ Background = (Brush) new BrushConverter().ConvertFrom("#00000000");
}
}
}
diff --git a/SafeExamBrowser.UserInterface/SafeExamBrowser.UserInterface.csproj b/SafeExamBrowser.UserInterface/SafeExamBrowser.UserInterface.csproj
index 0799683a..c172c8ab 100644
--- a/SafeExamBrowser.UserInterface/SafeExamBrowser.UserInterface.csproj
+++ b/SafeExamBrowser.UserInterface/SafeExamBrowser.UserInterface.csproj
@@ -164,7 +164,7 @@
Designer
MSBuild:Compile
-
+
Designer
MSBuild:Compile
diff --git a/SafeExamBrowser.UserInterface/Taskbar.xaml.cs b/SafeExamBrowser.UserInterface/Taskbar.xaml.cs
index adaf30cb..f4e7db12 100644
--- a/SafeExamBrowser.UserInterface/Taskbar.xaml.cs
+++ b/SafeExamBrowser.UserInterface/Taskbar.xaml.cs
@@ -24,6 +24,7 @@ namespace SafeExamBrowser.UserInterface
InitializeComponent();
Loaded += (o, args) => InitializeBounds();
+ Closing += Taskbar_Closing;
}
public void AddApplication(IApplicationButton button)
@@ -76,5 +77,16 @@ namespace SafeExamBrowser.UserInterface
logger.Info($"Set taskbar bounds to {Width}x{Height} at ({Left}/{Top}), in physical pixels: {size.X}x{size.Y} at ({position.X}/{position.Y}).");
});
}
+
+ private void Taskbar_Closing(object sender, System.ComponentModel.CancelEventArgs e)
+ {
+ foreach (var child in SystemControlStackPanel.Children)
+ {
+ if (child is ISystemControl)
+ {
+ (child as ISystemControl).Close();
+ }
+ }
+ }
}
}
diff --git a/SafeExamBrowser.UserInterface/Styles/ButtonStyles.xaml b/SafeExamBrowser.UserInterface/Templates/Buttons.xaml
similarity index 100%
rename from SafeExamBrowser.UserInterface/Styles/ButtonStyles.xaml
rename to SafeExamBrowser.UserInterface/Templates/Buttons.xaml
diff --git a/SafeExamBrowser/CompositionRoot.cs b/SafeExamBrowser/CompositionRoot.cs
index a329bf1b..23238abc 100644
--- a/SafeExamBrowser/CompositionRoot.cs
+++ b/SafeExamBrowser/CompositionRoot.cs
@@ -78,7 +78,7 @@ namespace SafeExamBrowser
displayMonitor = new DisplayMonitor(new ModuleLogger(logger, typeof(DisplayMonitor)), nativeMethods);
keyboardInterceptor = new KeyboardInterceptor(settings.Keyboard, new ModuleLogger(logger, typeof(KeyboardInterceptor)));
mouseInterceptor = new MouseInterceptor(new ModuleLogger(logger, typeof(MouseInterceptor)), settings.Mouse);
- powerSupply = new PowerSupply(new ModuleLogger(logger, typeof(PowerSupply)));
+ powerSupply = new PowerSupply(new ModuleLogger(logger, typeof(PowerSupply)), text);
processMonitor = new ProcessMonitor(new ModuleLogger(logger, typeof(ProcessMonitor)), nativeMethods);
windowMonitor = new WindowMonitor(new ModuleLogger(logger, typeof(WindowMonitor)), nativeMethods);