From 5b46dc125889cd3debb598e328640c9f17654f2a Mon Sep 17 00:00:00 2001 From: dbuechel Date: Tue, 14 Nov 2017 15:43:13 +0100 Subject: [PATCH] SEBWIN-110: Implemented basic version of wireless network system control. --- SafeExamBrowser.Contracts/I18n/TextKey.cs | 3 + .../Taskbar/ISystemWirelessNetworkControl.cs | 5 + SafeExamBrowser.Core/I18n/Text.xml | 3 + .../WirelessNetwork.cs | 94 ++++++++++++++----- .../WirelessNetworkDefinition.cs | 3 + .../Controls/WirelessNetworkButton.xaml | 4 +- .../Controls/WirelessNetworkControl.xaml | 11 ++- .../Controls/WirelessNetworkControl.xaml.cs | 36 ++++++- ...feExamBrowser.UserInterface.Classic.csproj | 4 + .../packages.config | 4 + SafeExamBrowser/CompositionRoot.cs | 2 +- 11 files changed, 140 insertions(+), 29 deletions(-) create mode 100644 SafeExamBrowser.UserInterface.Classic/packages.config diff --git a/SafeExamBrowser.Contracts/I18n/TextKey.cs b/SafeExamBrowser.Contracts/I18n/TextKey.cs index 2b84a3fd..841ece0f 100644 --- a/SafeExamBrowser.Contracts/I18n/TextKey.cs +++ b/SafeExamBrowser.Contracts/I18n/TextKey.cs @@ -50,6 +50,9 @@ namespace SafeExamBrowser.Contracts.I18n SystemControl_BatteryChargeLowInfo, SystemControl_BatteryRemainingCharge, SystemControl_KeyboardLayoutTooltip, + SystemControl_WirelessConnected, + SystemControl_WirelessDisconnected, + SystemControl_WirelessNotAvailable, Version } } diff --git a/SafeExamBrowser.Contracts/UserInterface/Taskbar/ISystemWirelessNetworkControl.cs b/SafeExamBrowser.Contracts/UserInterface/Taskbar/ISystemWirelessNetworkControl.cs index e6e9ffce..066e9a38 100644 --- a/SafeExamBrowser.Contracts/UserInterface/Taskbar/ISystemWirelessNetworkControl.cs +++ b/SafeExamBrowser.Contracts/UserInterface/Taskbar/ISystemWirelessNetworkControl.cs @@ -20,6 +20,11 @@ namespace SafeExamBrowser.Contracts.UserInterface.Taskbar /// bool HasWirelessNetworkAdapter { set; } + /// + /// Indicates to the user that a wireless network connection is being established. + /// + bool IsConnecting { set; } + /// /// Sets the current wireless network status. /// diff --git a/SafeExamBrowser.Core/I18n/Text.xml b/SafeExamBrowser.Core/I18n/Text.xml index cdb789b7..b9461f31 100644 --- a/SafeExamBrowser.Core/I18n/Text.xml +++ b/SafeExamBrowser.Core/I18n/Text.xml @@ -35,5 +35,8 @@ The battery charge is getting low. Consider connecting your computer to a power supply in time... %%HOURS%%h %%MINUTES%%min remaining (%%CHARGE%%%) Click to choose a different keyboard layout... + Connected to '%%NAME%%' + Disconnected + No wireless network adapter available or turned on Version \ No newline at end of file diff --git a/SafeExamBrowser.SystemComponents/WirelessNetwork.cs b/SafeExamBrowser.SystemComponents/WirelessNetwork.cs index 343f361a..01882a3e 100644 --- a/SafeExamBrowser.SystemComponents/WirelessNetwork.cs +++ b/SafeExamBrowser.SystemComponents/WirelessNetwork.cs @@ -8,7 +8,9 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Timers; +using SafeExamBrowser.Contracts.I18n; using SafeExamBrowser.Contracts.Logging; using SafeExamBrowser.Contracts.SystemComponents; using SafeExamBrowser.Contracts.UserInterface.Taskbar; @@ -22,14 +24,17 @@ namespace SafeExamBrowser.SystemComponents { private const int TWO_SECONDS = 2000; - private ILogger logger; private ISystemWirelessNetworkControl control; + private ILogger logger; + private IList networks = new List(); + private IText text; private Timer timer; private Wifi wifi; - public WirelessNetwork(ILogger logger) + public WirelessNetwork(ILogger logger, IText text) { this.logger = logger; + this.text = text; } public void Initialize(ISystemWirelessNetworkControl control) @@ -40,6 +45,7 @@ namespace SafeExamBrowser.SystemComponents if (wifi.NoWifiAvailable || IsTurnedOff()) { control.HasWirelessNetworkAdapter = false; + control.SetTooltip(text.Get(TextKey.SystemControl_WirelessNotAvailable)); logger.Info("Wireless networks cannot be monitored, as there is no hardware adapter available or it is turned off."); } else @@ -72,7 +78,33 @@ namespace SafeExamBrowser.SystemComponents private void Control_NetworkSelected(IWirelessNetwork network) { - throw new NotImplementedException(); + try + { + var accessPoint = networks.First(n => n.Id == network.Id).AccessPoint; + var authRequest = new AuthRequest(accessPoint); + + accessPoint.ConnectAsync(authRequest, false, (success) => AccessPoint_OnConnectComplete(network.Name, success)); + control.IsConnecting = true; + } + catch (Exception e) + { + logger.Error($"Failed to connect to wireless network '{network.Name}!'", e); + } + } + + private void AccessPoint_OnConnectComplete(string name, bool success) + { + if (success) + { + logger.Info($"Successfully connected to wireless network '{name}'."); + } + else + { + logger.Error($"Failed to connect to wireless network '{name}!'"); + } + + control.IsConnecting = false; + UpdateControl(); } private void Timer_Elapsed(object sender, ElapsedEventArgs e) @@ -82,7 +114,7 @@ namespace SafeExamBrowser.SystemComponents private void Wifi_ConnectionStatusChanged(object sender, WifiStatusEventArgs e) { - control.NetworkStatus = ToStatus(e.NewStatus); + UpdateControl(); } private bool IsTurnedOff() @@ -112,32 +144,50 @@ namespace SafeExamBrowser.SystemComponents private void UpdateControl() { - var networks = new List(); - - control.NetworkStatus = ToStatus(wifi.ConnectionStatus); - - try + lock (networks) { - foreach (var accessPoint in wifi.GetAccessPoints()) + try { - // The user may only connect to an already configured wireless network! - if (accessPoint.HasProfile) + networks.Clear(); + + foreach (var accessPoint in wifi.GetAccessPoints()) { - networks.Add(new WirelessNetworkDefinition + // The user may only connect to an already configured wireless network! + if (accessPoint.HasProfile) { - Name = accessPoint.Name, - SignalStrength = Convert.ToInt32(accessPoint.SignalStrength), - Status = accessPoint.IsConnected ? WirelessNetworkStatus.Connected : WirelessNetworkStatus.Disconnected - }); + networks.Add(ToDefinition(accessPoint)); + + if (accessPoint.IsConnected) + { + control.SetTooltip(text.Get(TextKey.SystemControl_WirelessConnected).Replace("%%NAME%%", accessPoint.Name)); + } + } } + + if (wifi.ConnectionStatus == WifiStatus.Disconnected) + { + control.SetTooltip(text.Get(TextKey.SystemControl_WirelessDisconnected)); + } + + control.NetworkStatus = ToStatus(wifi.ConnectionStatus); + control.Update(new List(networks)); + } + catch (Exception e) + { + logger.Error("Failed to update the wireless network adapter status!", e); } } - catch (Exception e) - { - logger.Error("Failed to update the wireless network adapter status!", e); - } + } - control.Update(networks); + private WirelessNetworkDefinition ToDefinition(AccessPoint accessPoint) + { + return new WirelessNetworkDefinition + { + AccessPoint = accessPoint, + Name = accessPoint.Name, + SignalStrength = Convert.ToInt32(accessPoint.SignalStrength), + Status = accessPoint.IsConnected ? WirelessNetworkStatus.Connected : WirelessNetworkStatus.Disconnected + }; } private WirelessNetworkStatus ToStatus(WifiStatus status) diff --git a/SafeExamBrowser.SystemComponents/WirelessNetworkDefinition.cs b/SafeExamBrowser.SystemComponents/WirelessNetworkDefinition.cs index 93fcc8f4..5a001558 100644 --- a/SafeExamBrowser.SystemComponents/WirelessNetworkDefinition.cs +++ b/SafeExamBrowser.SystemComponents/WirelessNetworkDefinition.cs @@ -8,11 +8,14 @@ using System; using SafeExamBrowser.Contracts.SystemComponents; +using SimpleWifi; namespace SafeExamBrowser.SystemComponents { internal class WirelessNetworkDefinition : IWirelessNetwork { + internal AccessPoint AccessPoint { get; set; } + public Guid Id { get; } public string Name { get; set; } public int SignalStrength { get; set; } diff --git a/SafeExamBrowser.UserInterface.Classic/Controls/WirelessNetworkButton.xaml b/SafeExamBrowser.UserInterface.Classic/Controls/WirelessNetworkButton.xaml index c7fcd7d3..6e2068bc 100644 --- a/SafeExamBrowser.UserInterface.Classic/Controls/WirelessNetworkButton.xaml +++ b/SafeExamBrowser.UserInterface.Classic/Controls/WirelessNetworkButton.xaml @@ -22,8 +22,8 @@ - - + + diff --git a/SafeExamBrowser.UserInterface.Classic/Controls/WirelessNetworkControl.xaml b/SafeExamBrowser.UserInterface.Classic/Controls/WirelessNetworkControl.xaml index 2f9be690..aaaff6ef 100644 --- a/SafeExamBrowser.UserInterface.Classic/Controls/WirelessNetworkControl.xaml +++ b/SafeExamBrowser.UserInterface.Classic/Controls/WirelessNetworkControl.xaml @@ -3,6 +3,7 @@ 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:fa="http://schemas.fontawesome.io/icons/" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:local="clr-namespace:SafeExamBrowser.UserInterface.Classic.Controls" mc:Ignorable="d" d:DesignHeight="40" d:DesignWidth="40"> @@ -26,7 +27,13 @@ diff --git a/SafeExamBrowser.UserInterface.Classic/Controls/WirelessNetworkControl.xaml.cs b/SafeExamBrowser.UserInterface.Classic/Controls/WirelessNetworkControl.xaml.cs index 0d907c85..357fdb8e 100644 --- a/SafeExamBrowser.UserInterface.Classic/Controls/WirelessNetworkControl.xaml.cs +++ b/SafeExamBrowser.UserInterface.Classic/Controls/WirelessNetworkControl.xaml.cs @@ -8,10 +8,13 @@ using System; using System.Collections.Generic; +using System.Windows; using System.Windows.Controls; using System.Windows.Media; +using FontAwesome.WPF; using SafeExamBrowser.Contracts.SystemComponents; using SafeExamBrowser.Contracts.UserInterface.Taskbar; +using SafeExamBrowser.UserInterface.Classic.Utilities; namespace SafeExamBrowser.UserInterface.Classic.Controls { @@ -24,6 +27,20 @@ namespace SafeExamBrowser.UserInterface.Classic.Controls Dispatcher.BeginInvoke(new Action(() => { Button.IsEnabled = value; + NoAdapterIcon.Visibility = value ? Visibility.Collapsed : Visibility.Visible; + })); + } + } + + public bool IsConnecting + { + set + { + Dispatcher.Invoke(new Action(() => + { + LoadingIcon.Visibility = value ? Visibility.Visible : Visibility.Collapsed; + SignalStrengthIcon.Visibility = value ? Visibility.Collapsed : Visibility.Visible; + NetworkStatusIcon.Visibility = value ? Visibility.Collapsed : Visibility.Visible; })); } } @@ -34,8 +51,10 @@ namespace SafeExamBrowser.UserInterface.Classic.Controls { Dispatcher.BeginInvoke(new Action(() => { - NetworkStatusIcon.Text = value == WirelessNetworkStatus.Connected ? "✔" : "❌"; - NetworkStatusIcon.Foreground = value == WirelessNetworkStatus.Connected ? Brushes.Green : Brushes.Red; + var icon = value == WirelessNetworkStatus.Connected ? FontAwesomeIcon.Check : FontAwesomeIcon.Close; + var brush = value == WirelessNetworkStatus.Connected ? Brushes.Green : Brushes.Orange; + + NetworkStatusIcon.Source = ImageAwesome.CreateImageSource(icon, brush); })); } } @@ -62,6 +81,8 @@ namespace SafeExamBrowser.UserInterface.Classic.Controls { Dispatcher.BeginInvoke(new Action(() => { + NetworksStackPanel.Children.Clear(); + foreach (var network in networks) { var button = new WirelessNetworkButton(); @@ -78,6 +99,7 @@ namespace SafeExamBrowser.UserInterface.Classic.Controls if (isCurrent) { NetworkStatus = network.Status; + SignalStrengthIcon.Child = GetIcon(network.SignalStrength); } NetworksStackPanel.Children.Add(button); @@ -89,6 +111,7 @@ namespace SafeExamBrowser.UserInterface.Classic.Controls { var originalBrush = Button.Background; + SignalStrengthIcon.Child = GetIcon(0); Button.Click += (o, args) => Popup.IsOpen = !Popup.IsOpen; Button.MouseLeave += (o, args) => Popup.IsOpen = Popup.IsMouseOver; Popup.MouseLeave += (o, args) => Popup.IsOpen = IsMouseOver; @@ -105,5 +128,14 @@ namespace SafeExamBrowser.UserInterface.Classic.Controls Button.Background = originalBrush; }; } + + private UIElement GetIcon(int signalStrength) + { + var icon = signalStrength > 66 ? "100" : (signalStrength > 33 ? "66" : (signalStrength > 0 ? "33" : "0")); + var uri = new Uri($"pack://application:,,,/SafeExamBrowser.UserInterface.Classic;component/Images/WiFi_{icon}.xaml"); + var resource = new XamlIconResource(uri); + + return IconResourceLoader.Load(resource); + } } } diff --git a/SafeExamBrowser.UserInterface.Classic/SafeExamBrowser.UserInterface.Classic.csproj b/SafeExamBrowser.UserInterface.Classic/SafeExamBrowser.UserInterface.Classic.csproj index aab5737f..68faf27a 100644 --- a/SafeExamBrowser.UserInterface.Classic/SafeExamBrowser.UserInterface.Classic.csproj +++ b/SafeExamBrowser.UserInterface.Classic/SafeExamBrowser.UserInterface.Classic.csproj @@ -50,6 +50,9 @@ MinimumRecommendedRules.ruleset + + ..\packages\FontAwesome.WPF.4.7.0.9\lib\net40\FontAwesome.WPF.dll + @@ -255,6 +258,7 @@ ResXFileCodeGenerator Resources.Designer.cs + SettingsSingleFileGenerator Settings.Designer.cs diff --git a/SafeExamBrowser.UserInterface.Classic/packages.config b/SafeExamBrowser.UserInterface.Classic/packages.config new file mode 100644 index 00000000..92648a5c --- /dev/null +++ b/SafeExamBrowser.UserInterface.Classic/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/SafeExamBrowser/CompositionRoot.cs b/SafeExamBrowser/CompositionRoot.cs index 3386f9b6..e914c5ae 100644 --- a/SafeExamBrowser/CompositionRoot.cs +++ b/SafeExamBrowser/CompositionRoot.cs @@ -82,7 +82,7 @@ namespace SafeExamBrowser 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); - wirelessNetwork = new WirelessNetwork(new ModuleLogger(logger, typeof(WirelessNetwork))); + wirelessNetwork = new WirelessNetwork(new ModuleLogger(logger, typeof(WirelessNetwork)), text); runtimeController = new RuntimeController(displayMonitor, new ModuleLogger(logger, typeof(RuntimeController)), processMonitor, Taskbar, windowMonitor); ShutdownController = new ShutdownController(logger, settings, text, uiFactory);