SEBWIN-141: Implemented power supply control for action center.

This commit is contained in:
dbuechel 2019-03-14 10:28:21 +01:00
parent 5ba6e6345c
commit ac2293dcb6
19 changed files with 213 additions and 75 deletions

View file

@ -110,21 +110,6 @@ namespace SafeExamBrowser.Client.Operations
InitializeWirelessNetworkForActionCenter();
InitializePowerSupplyForActionCenter();
//if (settings.AllowKeyboardLayout)
//{
// AddKeyboardLayoutControl();
//}
//if (settings.AllowWirelessNetwork)
//{
// AddWirelessNetworkControl();
//}
//if (systemInfo.HasBattery)
//{
// AddPowerSupplyControl();
//}
foreach (var activator in activators)
{
actionCenter.Register(activator);
@ -235,14 +220,20 @@ namespace SafeExamBrowser.Client.Operations
private void InitializePowerSupplyForActionCenter()
{
// TODO
if (systemInfo.HasBattery)
{
var control = uiFactory.CreatePowerSupplyControl(Location.ActionCenter);
powerSupply.Register(control);
actionCenter.AddSystemControl(control);
}
}
private void InitializePowerSupplyForTaskbar()
{
if (systemInfo.HasBattery)
{
var control = uiFactory.CreatePowerSupplyControl();
var control = uiFactory.CreatePowerSupplyControl(Location.Taskbar);
powerSupply.Register(control);
taskbar.AddSystemControl(control);

View file

@ -68,7 +68,7 @@ namespace SafeExamBrowser.Contracts.UserInterface
/// <summary>
/// Creates a system control displaying the power supply status of the computer.
/// </summary>
ISystemPowerSupplyControl CreatePowerSupplyControl();
ISystemPowerSupplyControl CreatePowerSupplyControl(Location location);
/// <summary>
/// Creates a new runtime window which runs on its own thread.

View file

@ -19,8 +19,8 @@ namespace SafeExamBrowser.Contracts.UserInterface.Shell
void Close();
/// <summary>
/// Sets the tooltip text of the system control.
/// Sets the information text of the system control.
/// </summary>
void SetTooltip(string text);
void SetInformation(string text);
}
}

View file

@ -34,7 +34,7 @@
Download Error
</Entry>
<Entry key="MessageBox_InvalidConfigurationData">
The configuration resource '%%URI%%' contains invalid data!
The configuration resource "%%URI%%" contains invalid data!
</Entry>
<Entry key="MessageBox_InvalidConfigurationDataTitle">
Configuration Error
@ -52,7 +52,7 @@
Invalid Quit Password
</Entry>
<Entry key="MessageBox_NotSupportedConfigurationResource">
The configuration resource '%%URI%%' is not supported!
The configuration resource "%%URI%%" is not supported!
</Entry>
<Entry key="MessageBox_NotSupportedConfigurationResourceTitle">
Configuration Error
@ -106,7 +106,7 @@
Startup Error
</Entry>
<Entry key="MessageBox_UnexpectedConfigurationError">
An unexpected error occurred while trying to load configuration resource '%%URI%%'! Please consult the application log for more information...
An unexpected error occurred while trying to load configuration resource "%%URI%%"! Please consult the application log for more information...
</Entry>
<Entry key="MessageBox_UnexpectedConfigurationErrorTitle">
Configuration Error
@ -262,10 +262,10 @@
%%HOURS%%h %%MINUTES%%min remaining (%%CHARGE%%%)
</Entry>
<Entry key="SystemControl_KeyboardLayoutTooltip">
Click to choose a different keyboard layout...
The current keyboard layout is "%%LAYOUT%%"
</Entry>
<Entry key="SystemControl_WirelessConnected">
Connected to '%%NAME%%'
Connected to "%%NAME%%"
</Entry>
<Entry key="SystemControl_WirelessDisconnected">
Disconnected

View file

@ -72,7 +72,7 @@ namespace SafeExamBrowser.SystemComponents
control.LayoutSelected += Control_LayoutSelected;
control.SetCurrent(currentLayout);
control.SetTooltip(text.Get(TextKey.SystemControl_KeyboardLayoutTooltip));
control.SetInformation(GetInfoTextFor(currentLayout));
controls.Add(control);
}
@ -97,8 +97,8 @@ namespace SafeExamBrowser.SystemComponents
{
var layout = layouts.First(l => l.Id == id);
logger.Info($"Changing keyboard layout to {ToString(layout.CultureInfo)}.");
InputLanguageManager.Current.CurrentInputLanguage = layout.CultureInfo;
logger.Info($"Changed keyboard layout to {ToString(layout.CultureInfo)}.");
}
private void Current_InputLanguageChanged(object sender, InputLanguageEventArgs e)
@ -111,9 +111,15 @@ namespace SafeExamBrowser.SystemComponents
foreach (var control in controls)
{
control.SetCurrent(newLayout);
control.SetInformation(GetInfoTextFor(newLayout));
}
}
private string GetInfoTextFor(KeyboardLayoutDefinition layout)
{
return text.Get(TextKey.SystemControl_KeyboardLayoutTooltip).Replace("%%LAYOUT%%", layout.CultureInfo.NativeName);
}
private string ToString(CultureInfo info)
{
return $"'{info.DisplayName}' ({info.ThreeLetterISOLanguageName.ToUpper()})";

View file

@ -78,46 +78,54 @@ namespace SafeExamBrowser.SystemComponents
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());
foreach (var control in controls)
{
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(control, 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());
control.SetBatteryCharge(charge, status);
control.SetPowerGridConnection(online);
control.SetTooltip(tooltip);
control.SetInformation(tooltip);
}
}
private void HandleBatteryStatus(ISystemPowerSupplyControl control, BatteryChargeStatus status)
private void HandleBatteryStatus(BatteryChargeStatus status)
{
if (status == BatteryChargeStatus.Low && !infoShown)
{
control.ShowLowBatteryInfo(text.Get(TextKey.SystemControl_BatteryChargeLowInfo));
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)
{
control.ShowCriticalBatteryWarning(text.Get(TextKey.SystemControl_BatteryChargeCriticalWarning));
foreach (var control in controls)
{
control.ShowCriticalBatteryWarning(text.Get(TextKey.SystemControl_BatteryChargeCriticalWarning));
}
warningShown = true;
logger.Warn("Warned the user about critical battery charge.");
}

View file

@ -72,7 +72,7 @@ namespace SafeExamBrowser.SystemComponents
else
{
control.HasWirelessNetworkAdapter = false;
control.SetTooltip(text.Get(TextKey.SystemControl_WirelessNotAvailable));
control.SetInformation(text.Get(TextKey.SystemControl_WirelessNotAvailable));
}
controls.Add(control);
@ -198,12 +198,12 @@ namespace SafeExamBrowser.SystemComponents
{
if (wifi.ConnectionStatus == WifiStatus.Disconnected)
{
control.SetTooltip(text.Get(TextKey.SystemControl_WirelessDisconnected));
control.SetInformation(text.Get(TextKey.SystemControl_WirelessDisconnected));
}
if (isConnected)
{
control.SetTooltip(text.Get(TextKey.SystemControl_WirelessConnected).Replace("%%NAME%%", networkName));
control.SetInformation(text.Get(TextKey.SystemControl_WirelessConnected).Replace("%%NAME%%", networkName));
}
control.NetworkStatus = ToStatus(wifi.ConnectionStatus);

View file

@ -15,9 +15,9 @@
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid Background="{StaticResource ActionCenterDarkBrush}" Height="64" Margin="2">
<Popup x:Name="Popup" AllowsTransparency="True" IsOpen="False" Placement="Top" PlacementTarget="{Binding ElementName=Button}">
<Border Background="{StaticResource ActionCenterDarkBrush}">
<Grid x:Name="Grid" Background="{StaticResource ActionCenterDarkBrush}" Height="64" Margin="2">
<Popup x:Name="Popup" IsOpen="False" Placement="Top" PlacementTarget="{Binding ElementName=Button}">
<Border Background="Gray">
<ScrollViewer x:Name="LayoutsScrollViewer" MaxHeight="250" VerticalScrollBarVisibility="Auto" Template="{StaticResource SmallBarScrollViewer}">
<StackPanel x:Name="LayoutsStackPanel" />
</ScrollViewer>

View file

@ -9,6 +9,7 @@
using System;
using System.Threading.Tasks;
using System.Windows.Controls;
using System.Windows.Media;
using SafeExamBrowser.Contracts.SystemComponents;
using SafeExamBrowser.Contracts.UserInterface.Shell;
using SafeExamBrowser.Contracts.UserInterface.Shell.Events;
@ -60,16 +61,20 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls
});
}
public void SetTooltip(string text)
public void SetInformation(string text)
{
Dispatcher.Invoke(() => Button.ToolTip = text);
}
private void InitializeKeyboardLayoutControl()
{
var originalBrush = Grid.Background;
Button.Click += (o, args) => Popup.IsOpen = !Popup.IsOpen;
Button.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => Popup.IsOpen = Popup.IsMouseOver));
Popup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => Popup.IsOpen = IsMouseOver));
Popup.Opened += (o, args) => Grid.Background = Brushes.Gray;
Popup.Closed += (o, args) => Grid.Background = originalBrush;
}
private void Button_LayoutSelected(Guid id)

View file

@ -0,0 +1,51 @@
<UserControl x:Class="SafeExamBrowser.UserInterface.Desktop.Controls.ActionCenterPowerSupplyControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:SafeExamBrowser.UserInterface.Desktop.Controls"
mc:Ignorable="d" d:DesignHeight="100" d:DesignWidth="125">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="../Templates/Buttons.xaml" />
<ResourceDictionary Source="../Templates/Colors.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid Background="{StaticResource ActionCenterDarkBrush}" Height="64" Margin="2">
<Button x:Name="Button" IsEnabled="False" Padding="2" ToolTipService.ShowOnDisabled="True" Template="{StaticResource ActionCenterButton}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="2*" />
<RowDefinition Height="3*" />
</Grid.RowDefinitions>
<Viewbox Grid.Row="0" Stretch="Uniform" Width="Auto">
<Canvas Height="40" Width="40">
<Viewbox Stretch="Uniform" Width="40" Panel.ZIndex="2">
<Canvas Width="1024.000" Height="1024.000">
<Canvas.LayoutTransform>
<RotateTransform Angle="180" />
</Canvas.LayoutTransform>
<Canvas>
<Path Data=" M 0.000,0.000 L 1024.000,0.000 L 1024.000,1024.000 L 0.000,1024.000 L 0.000,0.000 Z"/>
<Path StrokeThickness="35.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 112.000,300.000 L 951.000,300.000 C 970.330,300.000 986.000,315.670 986.000,335.000 L 986.000,689.000 C 986.000,708.330 970.330,724.000 951.000,724.000 L 112.000,724.000 C 92.670,724.000 77.000,708.330 77.000,689.000 L 77.000,335.000 C 77.000,315.670 92.670,300.000 112.000,300.000 Z"/>
<Path StrokeThickness="1.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Fill="#ff000000" Data=" M 25.000,462.500 L 50.000,462.500 C 52.760,462.500 55.000,464.740 55.000,467.500 L 55.000,557.500 C 55.000,560.260 52.760,562.500 50.000,562.500 L 25.000,562.500 C 22.240,562.500 20.000,560.260 20.000,557.500 L 20.000,467.500 C 20.000,464.740 22.240,462.500 25.000,462.500 Z"/>
</Canvas>
</Canvas>
</Viewbox>
<Rectangle x:Name="BatteryCharge" Canvas.Left="2" Canvas.Top="12" Fill="Green" Height="16" Width="35" Panel.ZIndex="1" />
<Canvas x:Name="PowerPlug" Panel.ZIndex="3" Canvas.Left="4" Canvas.Top="-3">
<Canvas.LayoutTransform>
<ScaleTransform ScaleX="2" ScaleY="2" />
</Canvas.LayoutTransform>
<Path Stroke="Black" StrokeStartLineCap="Round" Fill="Black" Data="M2.5,17.5 V10 Q5,10 5,6 H4 V4 H4 V6 H1 V4 H1 V6 H0 Q0,10 2.5,10" />
</Canvas>
<TextBlock x:Name="Warning" FontSize="36" FontWeight="ExtraBold" Foreground="Red" Canvas.Left="13" Canvas.Top="-7" Panel.ZIndex="3">!</TextBlock>
</Canvas>
</Viewbox>
<TextBlock Grid.Row="1" x:Name="Text" FontSize="11" Foreground="White" TextAlignment="Center" TextTrimming="CharacterEllipsis" TextWrapping="Wrap" VerticalAlignment="Bottom" />
</Grid>
</Button>
</Grid>
</UserControl>

View file

@ -0,0 +1,67 @@
/*
* 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.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using SafeExamBrowser.Contracts.SystemComponents;
using SafeExamBrowser.Contracts.UserInterface.Shell;
namespace SafeExamBrowser.UserInterface.Desktop.Controls
{
public partial class ActionCenterPowerSupplyControl : UserControl, ISystemPowerSupplyControl
{
private double BATTERY_CHARGE_MAX_WIDTH;
public ActionCenterPowerSupplyControl()
{
InitializeComponent();
BATTERY_CHARGE_MAX_WIDTH = BatteryCharge.Width;
}
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)
{
Dispatcher.InvokeAsync(() => Button.ToolTip = warning);
}
public void ShowLowBatteryInfo(string info)
{
Dispatcher.InvokeAsync(() => Button.ToolTip = info);
}
}
}

View file

@ -64,7 +64,7 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls
});
}
public void SetTooltip(string text)
public void SetInformation(string text)
{
Dispatcher.Invoke(() => Button.ToolTip = text);
}

View file

@ -1,4 +1,4 @@
<UserControl x:Class="SafeExamBrowser.UserInterface.Desktop.Controls.PowerSupplyControl"
<UserControl x:Class="SafeExamBrowser.UserInterface.Desktop.Controls.TaskbarPowerSupplyControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

View file

@ -15,11 +15,11 @@ using SafeExamBrowser.Contracts.UserInterface.Shell;
namespace SafeExamBrowser.UserInterface.Desktop.Controls
{
public partial class PowerSupplyControl : UserControl, ISystemPowerSupplyControl
public partial class TaskbarPowerSupplyControl : UserControl, ISystemPowerSupplyControl
{
private double BATTERY_CHARGE_MAX_WIDTH;
public PowerSupplyControl()
public TaskbarPowerSupplyControl()
{
InitializeComponent();
BATTERY_CHARGE_MAX_WIDTH = BatteryCharge.Width;
@ -51,7 +51,7 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls
Dispatcher.InvokeAsync(() => PowerPlug.Visibility = connected ? Visibility.Visible : Visibility.Collapsed);
}
public void SetTooltip(string text)
public void SetInformation(string text)
{
Dispatcher.InvokeAsync(() => Button.ToolTip = text);
}
@ -71,14 +71,12 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls
Popup.IsOpen = true;
PopupText.Text = text;
Background = Brushes.LightGray;
Button.IsEnabled = true;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
Popup.IsOpen = false;
Background = Brushes.Transparent;
Button.IsEnabled = false;
}
}
}

View file

@ -74,7 +74,7 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls
Popup.IsOpen = false;
}
public void SetTooltip(string text)
public void SetInformation(string text)
{
Dispatcher.InvokeAsync(() => Button.ToolTip = text);
}

View file

@ -89,6 +89,9 @@
<Compile Include="Controls\ActionCenterNotificationButton.xaml.cs">
<DependentUpon>ActionCenterNotificationButton.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\ActionCenterPowerSupplyControl.xaml.cs">
<DependentUpon>ActionCenterPowerSupplyControl.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\TaskbarApplicationControl.xaml.cs">
<DependentUpon>TaskbarApplicationControl.xaml</DependentUpon>
</Compile>
@ -107,8 +110,8 @@
<Compile Include="Controls\TaskbarNotificationButton.xaml.cs">
<DependentUpon>TaskbarNotificationButton.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\PowerSupplyControl.xaml.cs">
<DependentUpon>PowerSupplyControl.xaml</DependentUpon>
<Compile Include="Controls\TaskbarPowerSupplyControl.xaml.cs">
<DependentUpon>TaskbarPowerSupplyControl.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\QuitButton.xaml.cs">
<DependentUpon>QuitButton.xaml</DependentUpon>
@ -172,6 +175,10 @@
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Controls\ActionCenterPowerSupplyControl.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Controls\TaskbarApplicationControl.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
@ -196,7 +203,7 @@
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Controls\PowerSupplyControl.xaml">
<Page Include="Controls\TaskbarPowerSupplyControl.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>

View file

@ -1,6 +1,5 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:SafeExamBrowser.UserInterface.Desktop.Templates">
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<SolidColorBrush x:Key="BackgroundBrush">#FFF0F0F0</SolidColorBrush>
<SolidColorBrush x:Key="ActionCenterDarkBrush">#AA808080</SolidColorBrush>
</ResourceDictionary>

View file

@ -1,6 +1,5 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:SafeExamBrowser.UserInterface.Desktop.Templates">
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ControlTemplate x:Key="Thumb" TargetType="{x:Type Thumb}">
<Rectangle x:Name="Rectangle" Fill="DarkGray" Height="Auto" Width="Auto" />
<ControlTemplate.Triggers>

View file

@ -115,9 +115,16 @@ namespace SafeExamBrowser.UserInterface.Desktop
return Application.Current.Dispatcher.Invoke(() => new PasswordDialog(text.Get(message), text.Get(title), text));
}
public ISystemPowerSupplyControl CreatePowerSupplyControl()
public ISystemPowerSupplyControl CreatePowerSupplyControl(Location location)
{
return new PowerSupplyControl();
if (location == Location.ActionCenter)
{
return new ActionCenterPowerSupplyControl();
}
else
{
return new TaskbarPowerSupplyControl();
}
}
public IRuntimeWindow CreateRuntimeWindow(AppConfig appConfig)