SEBWIN-537: Implemented new network control showing wired as well as wireless network information.

This commit is contained in:
Damian Büchel 2022-04-19 18:21:29 +02:00
parent 3dda11956e
commit 6205bef251
57 changed files with 997 additions and 949 deletions

View file

@ -18,8 +18,8 @@ using SafeExamBrowser.Settings.Applications;
using SafeExamBrowser.SystemComponents.Contracts;
using SafeExamBrowser.SystemComponents.Contracts.Audio;
using SafeExamBrowser.SystemComponents.Contracts.Keyboard;
using SafeExamBrowser.SystemComponents.Contracts.Network;
using SafeExamBrowser.SystemComponents.Contracts.PowerSupply;
using SafeExamBrowser.SystemComponents.Contracts.WirelessNetwork;
using SafeExamBrowser.UserInterface.Contracts;
using SafeExamBrowser.UserInterface.Contracts.Shell;
@ -41,7 +41,7 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
private Mock<ITaskview> taskview;
private Mock<IText> text;
private Mock<IUserInterfaceFactory> uiFactory;
private Mock<IWirelessAdapter> wirelessAdapter;
private Mock<INetworkAdapter> networkAdapter;
private ShellOperation sut;
@ -55,13 +55,13 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
aboutNotification = new Mock<INotification>();
keyboard = new Mock<IKeyboard>();
logNotification = new Mock<INotification>();
networkAdapter = new Mock<INetworkAdapter>();
powerSupply = new Mock<IPowerSupply>();
systemInfo = new Mock<ISystemInfo>();
taskbar = new Mock<ITaskbar>();
taskview = new Mock<ITaskview>();
text = new Mock<IText>();
uiFactory = new Mock<IUserInterfaceFactory>();
wirelessAdapter = new Mock<IWirelessAdapter>();
context.Settings = new AppSettings();
@ -77,13 +77,13 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
keyboard.Object,
logger.Object,
logNotification.Object,
networkAdapter.Object,
powerSupply.Object,
systemInfo.Object,
taskbar.Object,
taskview.Object,
text.Object,
uiFactory.Object,
wirelessAdapter.Object);
uiFactory.Object);
}
[TestMethod]
@ -99,7 +99,7 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
context.Settings.ActionCenter.EnableActionCenter = true;
context.Settings.Keyboard.AllowAltTab = true;
context.Settings.Security.AllowTermination = true;
sut.Perform();
actionCenter.Verify(a => a.Register(It.Is<IActionCenterActivator>(a2 => a2 == actionCenterActivator.Object)), Times.Once);
@ -281,23 +281,23 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
context.Settings.ActionCenter.EnableActionCenter = true;
context.Settings.ActionCenter.ShowAudio = true;
context.Settings.ActionCenter.ShowKeyboardLayout = true;
context.Settings.ActionCenter.ShowWirelessNetwork = true;
context.Settings.ActionCenter.ShowNetwork = true;
context.Settings.Taskbar.EnableTaskbar = true;
context.Settings.Taskbar.ShowAudio = true;
context.Settings.Taskbar.ShowKeyboardLayout = true;
context.Settings.Taskbar.ShowWirelessNetwork = true;
context.Settings.Taskbar.ShowNetwork = true;
systemInfo.SetupGet(s => s.HasBattery).Returns(true);
uiFactory.Setup(f => f.CreateAudioControl(It.IsAny<IAudio>(), It.IsAny<Location>())).Returns(new Mock<ISystemControl>().Object);
uiFactory.Setup(f => f.CreateKeyboardLayoutControl(It.IsAny<IKeyboard>(), It.IsAny<Location>())).Returns(new Mock<ISystemControl>().Object);
uiFactory.Setup(f => f.CreatePowerSupplyControl(It.IsAny<IPowerSupply>(), It.IsAny<Location>())).Returns(new Mock<ISystemControl>().Object);
uiFactory.Setup(f => f.CreateWirelessNetworkControl(It.IsAny<IWirelessAdapter>(), It.IsAny<Location>())).Returns(new Mock<ISystemControl>().Object);
uiFactory.Setup(f => f.CreateNetworkControl(It.IsAny<INetworkAdapter>(), It.IsAny<Location>())).Returns(new Mock<ISystemControl>().Object);
sut.Perform();
audio.Verify(a => a.Initialize(), Times.Once);
powerSupply.Verify(p => p.Initialize(), Times.Once);
wirelessAdapter.Verify(w => w.Initialize(), Times.Once);
networkAdapter.Verify(w => w.Initialize(), Times.Once);
keyboard.Verify(k => k.Initialize(), Times.Once);
actionCenter.Verify(a => a.AddSystemControl(It.IsAny<ISystemControl>()), Times.Exactly(4));
taskbar.Verify(t => t.AddSystemControl(It.IsAny<ISystemControl>()), Times.Exactly(4));
@ -309,23 +309,23 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
context.Settings.ActionCenter.EnableActionCenter = true;
context.Settings.ActionCenter.ShowAudio = false;
context.Settings.ActionCenter.ShowKeyboardLayout = false;
context.Settings.ActionCenter.ShowWirelessNetwork = false;
context.Settings.ActionCenter.ShowNetwork = false;
context.Settings.Taskbar.EnableTaskbar = true;
context.Settings.Taskbar.ShowAudio = false;
context.Settings.Taskbar.ShowKeyboardLayout = false;
context.Settings.Taskbar.ShowWirelessNetwork = false;
context.Settings.Taskbar.ShowNetwork = false;
systemInfo.SetupGet(s => s.HasBattery).Returns(false);
uiFactory.Setup(f => f.CreateAudioControl(It.IsAny<IAudio>(), It.IsAny<Location>())).Returns(new Mock<ISystemControl>().Object);
uiFactory.Setup(f => f.CreateKeyboardLayoutControl(It.IsAny<IKeyboard>(), It.IsAny<Location>())).Returns(new Mock<ISystemControl>().Object);
uiFactory.Setup(f => f.CreatePowerSupplyControl(It.IsAny<IPowerSupply>(), It.IsAny<Location>())).Returns(new Mock<ISystemControl>().Object);
uiFactory.Setup(f => f.CreateWirelessNetworkControl(It.IsAny<IWirelessAdapter>(), It.IsAny<Location>())).Returns(new Mock<ISystemControl>().Object);
uiFactory.Setup(f => f.CreateNetworkControl(It.IsAny<INetworkAdapter>(), It.IsAny<Location>())).Returns(new Mock<ISystemControl>().Object);
sut.Perform();
audio.Verify(a => a.Initialize(), Times.Once);
powerSupply.Verify(p => p.Initialize(), Times.Once);
wirelessAdapter.Verify(w => w.Initialize(), Times.Once);
networkAdapter.Verify(w => w.Initialize(), Times.Once);
keyboard.Verify(k => k.Initialize(), Times.Once);
actionCenter.Verify(a => a.AddSystemControl(It.IsAny<ISystemControl>()), Times.Never);
taskbar.Verify(t => t.AddSystemControl(It.IsAny<ISystemControl>()), Times.Never);
@ -408,7 +408,7 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
logNotification.Verify(c => c.Terminate(), Times.Once);
powerSupply.Verify(p => p.Terminate(), Times.Once);
keyboard.Verify(k => k.Terminate(), Times.Once);
wirelessAdapter.Verify(w => w.Terminate(), Times.Once);
networkAdapter.Verify(w => w.Terminate(), Times.Once);
}
}
}

View file

@ -38,11 +38,11 @@ using SafeExamBrowser.Settings.UserInterface;
using SafeExamBrowser.SystemComponents;
using SafeExamBrowser.SystemComponents.Audio;
using SafeExamBrowser.SystemComponents.Contracts;
using SafeExamBrowser.SystemComponents.Contracts.Network;
using SafeExamBrowser.SystemComponents.Contracts.PowerSupply;
using SafeExamBrowser.SystemComponents.Contracts.WirelessNetwork;
using SafeExamBrowser.SystemComponents.Keyboard;
using SafeExamBrowser.SystemComponents.Network;
using SafeExamBrowser.SystemComponents.PowerSupply;
using SafeExamBrowser.SystemComponents.WirelessNetwork;
using SafeExamBrowser.UserInterface.Contracts;
using SafeExamBrowser.UserInterface.Contracts.FileSystemDialog;
using SafeExamBrowser.UserInterface.Contracts.MessageBox;
@ -71,6 +71,7 @@ namespace SafeExamBrowser.Client
private ILogger logger;
private IMessageBox messageBox;
private INativeMethods nativeMethods;
private INetworkAdapter networkAdapter;
private IPowerSupply powerSupply;
private IRuntimeProxy runtimeProxy;
private ISystemInfo systemInfo;
@ -79,7 +80,6 @@ namespace SafeExamBrowser.Client
private IUserInfo userInfo;
private IText text;
private IUserInterfaceFactory uiFactory;
private IWirelessAdapter wirelessAdapter;
internal ClientController ClientController { get; private set; }
@ -96,13 +96,13 @@ namespace SafeExamBrowser.Client
context = new ClientContext();
messageBox = BuildMessageBox();
nativeMethods = new NativeMethods();
networkAdapter = new NetworkAdapter(ModuleLogger(nameof(NetworkAdapter)), nativeMethods);
powerSupply = new PowerSupply(ModuleLogger(nameof(PowerSupply)));
runtimeProxy = new RuntimeProxy(runtimeHostUri, new ProxyObjectFactory(), ModuleLogger(nameof(RuntimeProxy)), Interlocutor.Client);
systemInfo = new SystemInfo();
taskbar = uiFactory.CreateTaskbar(ModuleLogger("Taskbar"));
taskview = uiFactory.CreateTaskview();
userInfo = new UserInfo(ModuleLogger(nameof(UserInfo)));
wirelessAdapter = new WirelessAdapter(ModuleLogger(nameof(WirelessAdapter)));
var processFactory = new ProcessFactory(ModuleLogger(nameof(ProcessFactory)));
var applicationMonitor = new ApplicationMonitor(TWO_SECONDS, ModuleLogger(nameof(ApplicationMonitor)), nativeMethods, processFactory);
@ -157,13 +157,13 @@ namespace SafeExamBrowser.Client
internal void LogStartupInformation()
{
logger.Log($"# New client instance started at {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}");
logger.Log($"# New client instance started at {DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}");
logger.Log(string.Empty);
}
internal void LogShutdownInformation()
{
logger?.Log($"# Client instance terminated at {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}");
logger?.Log($"# Client instance terminated at {DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}");
}
private void ValidateCommandLineArguments()
@ -271,7 +271,7 @@ namespace SafeExamBrowser.Client
private IOperation BuildServerOperation()
{
var server = new ServerProxy(context.AppConfig, ModuleLogger(nameof(ServerProxy)), systemInfo, userInfo, powerSupply, wirelessAdapter);
var server = new ServerProxy(context.AppConfig, ModuleLogger(nameof(ServerProxy)), systemInfo, userInfo, powerSupply, networkAdapter);
var operation = new ServerOperation(context, logger, server);
context.Server = server;
@ -293,13 +293,13 @@ namespace SafeExamBrowser.Client
keyboard,
logger,
logNotification,
networkAdapter,
powerSupply,
systemInfo,
taskbar,
taskview,
text,
uiFactory,
wirelessAdapter);
uiFactory);
context.Activators.Add(new ActionCenterKeyboardActivator(ModuleLogger(nameof(ActionCenterKeyboardActivator)), nativeMethods));
context.Activators.Add(new ActionCenterTouchActivator(ModuleLogger(nameof(ActionCenterTouchActivator)), nativeMethods));

View file

@ -15,8 +15,8 @@ using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.SystemComponents.Contracts;
using SafeExamBrowser.SystemComponents.Contracts.Audio;
using SafeExamBrowser.SystemComponents.Contracts.Keyboard;
using SafeExamBrowser.SystemComponents.Contracts.Network;
using SafeExamBrowser.SystemComponents.Contracts.PowerSupply;
using SafeExamBrowser.SystemComponents.Contracts.WirelessNetwork;
using SafeExamBrowser.UserInterface.Contracts;
using SafeExamBrowser.UserInterface.Contracts.Shell;
@ -24,19 +24,19 @@ namespace SafeExamBrowser.Client.Operations
{
internal class ShellOperation : ClientOperation
{
private IActionCenter actionCenter;
private IAudio audio;
private INotification aboutNotification;
private IKeyboard keyboard;
private ILogger logger;
private INotification logNotification;
private IPowerSupply powerSupply;
private ISystemInfo systemInfo;
private ITaskbar taskbar;
private ITaskview taskview;
private IText text;
private IUserInterfaceFactory uiFactory;
private IWirelessAdapter wirelessAdapter;
private readonly IActionCenter actionCenter;
private readonly IAudio audio;
private readonly INotification aboutNotification;
private readonly IKeyboard keyboard;
private readonly ILogger logger;
private readonly INotification logNotification;
private readonly INetworkAdapter networkAdapter;
private readonly IPowerSupply powerSupply;
private readonly ISystemInfo systemInfo;
private readonly ITaskbar taskbar;
private readonly ITaskview taskview;
private readonly IText text;
private readonly IUserInterfaceFactory uiFactory;
public override event ActionRequiredEventHandler ActionRequired { add { } remove { } }
public override event StatusChangedEventHandler StatusChanged;
@ -49,13 +49,13 @@ namespace SafeExamBrowser.Client.Operations
IKeyboard keyboard,
ILogger logger,
INotification logNotification,
INetworkAdapter networkAdapter,
IPowerSupply powerSupply,
ISystemInfo systemInfo,
ITaskbar taskbar,
ITaskview taskview,
IText text,
IUserInterfaceFactory uiFactory,
IWirelessAdapter wirelessAdapter) : base(context)
IUserInterfaceFactory uiFactory) : base(context)
{
this.aboutNotification = aboutNotification;
this.actionCenter = actionCenter;
@ -63,13 +63,13 @@ namespace SafeExamBrowser.Client.Operations
this.keyboard = keyboard;
this.logger = logger;
this.logNotification = logNotification;
this.networkAdapter = networkAdapter;
this.powerSupply = powerSupply;
this.systemInfo = systemInfo;
this.text = text;
this.taskbar = taskbar;
this.taskview = taskview;
this.uiFactory = uiFactory;
this.wirelessAdapter = wirelessAdapter;
}
public override OperationResult Perform()
@ -134,7 +134,7 @@ namespace SafeExamBrowser.Client.Operations
InitializeClockForActionCenter();
InitializeLogNotificationForActionCenter();
InitializeKeyboardLayoutForActionCenter();
InitializeWirelessNetworkForActionCenter();
InitializeNetworkForActionCenter();
InitializePowerSupplyForActionCenter();
InitializeQuitButtonForActionCenter();
}
@ -155,7 +155,7 @@ namespace SafeExamBrowser.Client.Operations
InitializeAboutNotificationForTaskbar();
InitializeLogNotificationForTaskbar();
InitializePowerSupplyForTaskbar();
InitializeWirelessNetworkForTaskbar();
InitializeNetworkForTaskbar();
InitializeAudioForTaskbar();
InitializeKeyboardLayoutForTaskbar();
InitializeClockForTaskbar();
@ -204,8 +204,8 @@ namespace SafeExamBrowser.Client.Operations
{
audio.Initialize();
keyboard.Initialize();
networkAdapter.Initialize();
powerSupply.Initialize();
wirelessAdapter.Initialize();
}
private void InitializeAboutNotificationForActionCenter()
@ -308,19 +308,19 @@ namespace SafeExamBrowser.Client.Operations
taskbar.ShowQuitButton = Context.Settings.Security.AllowTermination;
}
private void InitializeWirelessNetworkForActionCenter()
private void InitializeNetworkForActionCenter()
{
if (Context.Settings.ActionCenter.ShowWirelessNetwork)
if (Context.Settings.ActionCenter.ShowNetwork)
{
actionCenter.AddSystemControl(uiFactory.CreateWirelessNetworkControl(wirelessAdapter, Location.ActionCenter));
actionCenter.AddSystemControl(uiFactory.CreateNetworkControl(networkAdapter, Location.ActionCenter));
}
}
private void InitializeWirelessNetworkForTaskbar()
private void InitializeNetworkForTaskbar()
{
if (Context.Settings.Taskbar.ShowWirelessNetwork)
if (Context.Settings.Taskbar.ShowNetwork)
{
taskbar.AddSystemControl(uiFactory.CreateWirelessNetworkControl(wirelessAdapter, Location.Taskbar));
taskbar.AddSystemControl(uiFactory.CreateNetworkControl(networkAdapter, Location.Taskbar));
}
}
@ -342,8 +342,8 @@ namespace SafeExamBrowser.Client.Operations
{
audio.Terminate();
keyboard.Terminate();
networkAdapter.Terminate();
powerSupply.Terminate();
wirelessAdapter.Terminate();
}
}
}

View file

@ -30,7 +30,6 @@ namespace SafeExamBrowser.Configuration.UnitTests
private Mock<ICertificateStore> certificateStore;
private Mock<IResourceLoader> fileLoader;
private Mock<IResourceSaver> fileSaver;
private Mock<IHashAlgorithm> hashAlgorithm;
private Mock<IModuleLogger> logger;
private Mock<IResourceLoader> networkLoader;
private Mock<IDataParser> xmlParser;
@ -44,7 +43,6 @@ namespace SafeExamBrowser.Configuration.UnitTests
certificateStore = new Mock<ICertificateStore>();
fileLoader = new Mock<IResourceLoader>();
fileSaver = new Mock<IResourceSaver>();
hashAlgorithm = new Mock<IHashAlgorithm>();
logger = new Mock<IModuleLogger>();
networkLoader = new Mock<IResourceLoader>();
xmlParser = new Mock<IDataParser>();
@ -56,7 +54,7 @@ namespace SafeExamBrowser.Configuration.UnitTests
SetEntryAssembly();
sut = new ConfigurationRepository(certificateStore.Object, hashAlgorithm.Object, logger.Object);
sut = new ConfigurationRepository(certificateStore.Object, logger.Object);
sut.InitializeAppConfig();
}

View file

@ -29,8 +29,8 @@ namespace SafeExamBrowser.Configuration.ConfigurationData.DataMapping
case Keys.UserInterface.ShowKeyboardLayout:
MapShowKeyboardLayout(settings, value);
break;
case Keys.UserInterface.ShowWirelessNetwork:
MapShowWirelessNetwork(settings, value);
case Keys.UserInterface.ShowNetwork:
MapShowNetwork(settings, value);
break;
case Keys.UserInterface.Taskbar.EnableTaskbar:
MapEnableTaskbar(settings, value);
@ -79,12 +79,12 @@ namespace SafeExamBrowser.Configuration.ConfigurationData.DataMapping
}
}
private void MapShowWirelessNetwork(AppSettings settings, object value)
private void MapShowNetwork(AppSettings settings, object value)
{
if (value is bool show)
{
settings.ActionCenter.ShowWirelessNetwork = show;
settings.Taskbar.ShowWirelessNetwork = show;
settings.ActionCenter.ShowNetwork = show;
settings.Taskbar.ShowNetwork = show;
}
}

View file

@ -101,9 +101,9 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
settings.ActionCenter.EnableActionCenter = true;
settings.ActionCenter.ShowApplicationInfo = true;
settings.ActionCenter.ShowApplicationLog = false;
settings.ActionCenter.ShowKeyboardLayout = true;
settings.ActionCenter.ShowWirelessNetwork = false;
settings.ActionCenter.ShowClock = true;
settings.ActionCenter.ShowKeyboardLayout = true;
settings.ActionCenter.ShowNetwork = false;
settings.Applications.Blacklist.Add(new BlacklistApplication { ExecutableName = "AA_v3.exe", OriginalName = "AA_v3.exe" });
settings.Applications.Blacklist.Add(new BlacklistApplication { ExecutableName = "AeroAdmin.exe", OriginalName = "AeroAdmin.exe" });
@ -283,12 +283,12 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
settings.Taskbar.EnableTaskbar = true;
settings.Taskbar.ShowApplicationInfo = false;
settings.Taskbar.ShowApplicationLog = false;
settings.Taskbar.ShowKeyboardLayout = true;
settings.Taskbar.ShowWirelessNetwork = false;
settings.Taskbar.ShowClock = true;
settings.Taskbar.ShowKeyboardLayout = true;
settings.Taskbar.ShowNetwork = false;
settings.UserInterfaceMode = UserInterfaceMode.Desktop;
return settings;
}
}

View file

@ -327,7 +327,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
internal const string ShowAudio = "audioControlEnabled";
internal const string ShowClock = "showTime";
internal const string ShowKeyboardLayout = "showInputLanguage";
internal const string ShowWirelessNetwork = "allowWlan";
internal const string ShowNetwork = "allowWlan";
internal const string UserInterfaceMode = "touchOptimized";
internal static class ActionCenter

View file

@ -22,21 +22,19 @@ namespace SafeExamBrowser.Configuration
{
public class ConfigurationRepository : IConfigurationRepository
{
private ICertificateStore certificateStore;
private IList<IDataParser> dataParsers;
private IList<IDataSerializer> dataSerializers;
private DataMapper dataMapper;
private DataProcessor dataProcessor;
private DataValues dataValues;
private IHashAlgorithm hashAlgorithm;
private ILogger logger;
private IList<IResourceLoader> resourceLoaders;
private IList<IResourceSaver> resourceSavers;
private readonly ICertificateStore certificateStore;
private readonly IList<IDataParser> dataParsers;
private readonly IList<IDataSerializer> dataSerializers;
private readonly DataMapper dataMapper;
private readonly DataProcessor dataProcessor;
private readonly DataValues dataValues;
private readonly ILogger logger;
private readonly IList<IResourceLoader> resourceLoaders;
private readonly IList<IResourceSaver> resourceSavers;
public ConfigurationRepository(ICertificateStore certificateStore, IHashAlgorithm hashAlgorithm, IModuleLogger logger)
public ConfigurationRepository(ICertificateStore certificateStore, IModuleLogger logger)
{
this.certificateStore = certificateStore;
this.hashAlgorithm = hashAlgorithm;
this.logger = logger;
dataParsers = new List<IDataParser>();
@ -171,7 +169,7 @@ namespace SafeExamBrowser.Configuration
var status = LoadStatus.NotSupported;
var resourceLoader = resourceLoaders.FirstOrDefault(l => l.CanLoad(resource));
data = default(Stream);
data = default;
if (resourceLoader != null)
{
@ -191,8 +189,8 @@ namespace SafeExamBrowser.Configuration
var parser = dataParsers.FirstOrDefault(p => p.CanParse(data));
var status = LoadStatus.NotSupported;
encryption = default(EncryptionParameters);
format = default(FormatType);
encryption = default;
format = default;
rawData = default(Dictionary<string, object>);
if (parser != null)
@ -237,7 +235,7 @@ namespace SafeExamBrowser.Configuration
var serializer = dataSerializers.FirstOrDefault(s => s.CanSerialize(format));
var status = SaveStatus.NotSupported;
serialized = default(Stream);
serialized = default;
if (serializer != null)
{

View file

@ -213,10 +213,11 @@ namespace SafeExamBrowser.I18n.Contracts
SystemControl_BatteryChargeLowInfo,
SystemControl_BatteryRemainingCharge,
SystemControl_KeyboardLayoutTooltip,
SystemControl_WirelessConnected,
SystemControl_WirelessConnecting,
SystemControl_WirelessDisconnected,
SystemControl_WirelessNotAvailable,
SystemControl_NetworkDisconnected,
SystemControl_NetworkNotAvailable,
SystemControl_NetworkWiredConnected,
SystemControl_NetworkWirelessConnected,
SystemControl_NetworkWirelessConnecting,
Version
}
}

View file

@ -597,17 +597,20 @@
<Entry key="SystemControl_KeyboardLayoutTooltip">
Das aktuelle Tastatur-Layout ist "%%LAYOUT%%"
</Entry>
<Entry key="SystemControl_WirelessConnected">
Verbunden mit "%%NAME%%"
</Entry>
<Entry key="SystemControl_WirelessConnecting">
Verbinde...
</Entry>
<Entry key="SystemControl_WirelessDisconnected">
<Entry key="SystemControl_NetworkDisconnected">
Getrennt
</Entry>
<Entry key="SystemControl_WirelessNotAvailable">
Kein WLAN-Netzwerkadapter verfügbar oder eingestellt
<Entry key="SystemControl_NetworkNotAvailable">
Kein Netzwerkadapter verfügbar oder eingestellt
</Entry>
<Entry key="SystemControl_NetworkWiredConnected">
Verbunden
</Entry>
<Entry key="SystemControl_NetworkWirelessConnected">
Verbunden mit "%%NAME%%"
</Entry>
<Entry key="SystemControl_NetworkWirelessConnecting">
Verbinde...
</Entry>
<Entry key="Version">
Version

View file

@ -597,18 +597,21 @@
<Entry key="SystemControl_KeyboardLayoutTooltip">
The current keyboard layout is "%%LAYOUT%%"
</Entry>
<Entry key="SystemControl_WirelessConnected">
Connected to "%%NAME%%"
</Entry>
<Entry key="SystemControl_WirelessConnecting">
Connecting...
</Entry>
<Entry key="SystemControl_WirelessDisconnected">
<Entry key="SystemControl_NetworkDisconnected">
Disconnected
</Entry>
<Entry key="SystemControl_WirelessNotAvailable">
<Entry key="SystemControl_NetworkNotAvailable">
No wireless network adapter available or turned on
</Entry>
<Entry key="SystemControl_NetworkWiredConnected">
Connected
</Entry>
<Entry key="SystemControl_NetworkWirelessConnected">
Connected to "%%NAME%%"
</Entry>
<Entry key="SystemControl_NetworkWirelessConnecting">
Connecting...
</Entry>
<Entry key="Version">
Version
</Entry>

View file

@ -597,18 +597,21 @@
<Entry key="SystemControl_KeyboardLayoutTooltip">
La disposition actuelle du clavier est "%%LAYOUT%%"
</Entry>
<Entry key="SystemControl_WirelessConnected">
Connecté à "%%NAME%%"
</Entry>
<Entry key="SystemControl_WirelessConnecting">
Connection en cours...
</Entry>
<Entry key="SystemControl_WirelessDisconnected">
<Entry key="SystemControl_NetworkDisconnected">
Déconnecté
</Entry>
<Entry key="SystemControl_WirelessNotAvailable">
<Entry key="SystemControl_NetworkNotAvailable">
Pas d'adaptateur réseau sans fil disponible ou allumé
</Entry>
<Entry key="SystemControl_NetworkWiredConnected">
Connecté
</Entry>
<Entry key="SystemControl_NetworkWirelessConnected">
Connecté à "%%NAME%%"
</Entry>
<Entry key="SystemControl_NetworkWirelessConnecting">
Connection en cours...
</Entry>
<Entry key="Version">
Version
</Entry>

View file

@ -597,18 +597,21 @@
<Entry key="SystemControl_KeyboardLayoutTooltip">
L'attuale layout della tastiera è "%%LAYOUT%%"
</Entry>
<Entry key="SystemControl_WirelessConnected">
Collegato a "%%NAME%%"
</Entry>
<Entry key="SystemControl_WirelessConnecting">
Collegamento...
</Entry>
<Entry key="SystemControl_WirelessDisconnected">
<Entry key="SystemControl_NetworkDisconnected">
Disconnesso
</Entry>
<Entry key="SystemControl_WirelessNotAvailable">
<Entry key="SystemControl_NetworkNotAvailable">
Nessun adattatore di rete wireless disponibile o attivato
</Entry>
<Entry key="SystemControl_NetworkWiredConnected">
Collegato
</Entry>
<Entry key="SystemControl_NetworkWirelessConnected">
Collegato a "%%NAME%%"
</Entry>
<Entry key="SystemControl_NetworkWirelessConnecting">
Collegamento...
</Entry>
<Entry key="Version">
Versione
</Entry>

View file

@ -531,18 +531,21 @@
<Entry key="SystemControl_KeyboardLayoutTooltip">
当前键盘布局是 "%%LAYOUT%%"
</Entry>
<Entry key="SystemControl_WirelessConnected">
已连接到 "%%NAME%%"
</Entry>
<Entry key="SystemControl_WirelessConnecting">
正在连接...
</Entry>
<Entry key="SystemControl_WirelessDisconnected">
<Entry key="SystemControl_NetworkDisconnected">
连接已断开
</Entry>
<Entry key="SystemControl_WirelessNotAvailable">
<Entry key="SystemControl_NetworkNotAvailable">
没有无线网卡或未启用无线网卡
</Entry>
<Entry key="SystemControl_NetworkWiredConnected">
连接的
</Entry>
<Entry key="SystemControl_NetworkWirelessConnected">
已连接到 "%%NAME%%"
</Entry>
<Entry key="SystemControl_NetworkWirelessConnecting">
正在连接...
</Entry>
<Entry key="Version">
版本
</Entry>

View file

@ -149,7 +149,7 @@ namespace SafeExamBrowser.Runtime
var xmlParser = new XmlParser(compressor, ModuleLogger(nameof(XmlParser)));
var xmlSerializer = new XmlSerializer(ModuleLogger(nameof(XmlSerializer)));
configuration = new ConfigurationRepository(certificateStore, new HashAlgorithm(), repositoryLogger);
configuration = new ConfigurationRepository(certificateStore, repositoryLogger);
appConfig = configuration.InitializeAppConfig();
configuration.Register(new BinaryParser(

View file

@ -26,8 +26,8 @@ using SafeExamBrowser.Server.Data;
using SafeExamBrowser.Settings.Logging;
using SafeExamBrowser.Settings.Server;
using SafeExamBrowser.SystemComponents.Contracts;
using SafeExamBrowser.SystemComponents.Contracts.Network;
using SafeExamBrowser.SystemComponents.Contracts.PowerSupply;
using SafeExamBrowser.SystemComponents.Contracts.WirelessNetwork;
using Timer = System.Timers.Timer;
namespace SafeExamBrowser.Server
@ -45,7 +45,7 @@ namespace SafeExamBrowser.Server
private readonly IPowerSupply powerSupply;
private readonly ISystemInfo systemInfo;
private readonly IUserInfo userInfo;
private readonly IWirelessAdapter wirelessAdapter;
private readonly INetworkAdapter networkAdapter;
private ApiVersion1 api;
private string connectionToken;
@ -70,7 +70,7 @@ namespace SafeExamBrowser.Server
ISystemInfo systemInfo,
IUserInfo userInfo,
IPowerSupply powerSupply = default,
IWirelessAdapter wirelessAdapter = default)
INetworkAdapter networkAdapter = default)
{
this.api = new ApiVersion1();
this.appConfig = appConfig;
@ -79,12 +79,12 @@ namespace SafeExamBrowser.Server
this.logger = logger;
this.logContent = new ConcurrentQueue<ILogContent>();
this.logTimer = new Timer();
this.networkAdapter = networkAdapter;
this.parser = new Parser(logger);
this.pingTimer = new Timer();
this.powerSupply = powerSupply;
this.systemInfo = systemInfo;
this.userInfo = userInfo;
this.wirelessAdapter = wirelessAdapter;
}
public ServerResponse Connect()
@ -226,7 +226,7 @@ namespace SafeExamBrowser.Server
public void Initialize(ServerSettings settings)
{
this.settings = settings;
httpClient = new HttpClient();
httpClient.BaseAddress = new Uri(settings.ServerUrl);
@ -348,20 +348,20 @@ namespace SafeExamBrowser.Server
pingTimer.Start();
logger.Info("Started sending pings.");
if (powerSupply != default(IPowerSupply) && wirelessAdapter != default(IWirelessAdapter))
if (powerSupply != default && networkAdapter != default)
{
powerSupply.StatusChanged += PowerSupply_StatusChanged;
wirelessAdapter.NetworksChanged += WirelessAdapter_NetworksChanged;
networkAdapter.Changed += NetworkAdapter_Changed;
logger.Info("Started monitoring system components.");
}
}
public void StopConnectivity()
{
if (powerSupply != default(IPowerSupply) && wirelessAdapter != default(IWirelessAdapter))
if (powerSupply != default && networkAdapter != default)
{
powerSupply.StatusChanged -= PowerSupply_StatusChanged;
wirelessAdapter.NetworksChanged -= WirelessAdapter_NetworksChanged;
networkAdapter.Changed -= NetworkAdapter_Changed;
logger.Info("Stopped monitoring system components.");
}
@ -477,7 +477,7 @@ namespace SafeExamBrowser.Server
SendPowerSupplyStatus(text, value);
currentPowerSupplyValue = value;
}
else if (connected != connectedToPowergrid)
else if (connected != connectedToPowergrid)
{
var text = $"<battery> Device has been {(connected ? "connected to" : "disconnected from")} power grid";
SendPowerSupplyStatus(text, value);
@ -507,13 +507,13 @@ namespace SafeExamBrowser.Server
TryExecute(HttpMethod.Post, api.LogEndpoint, out _, content, contentType, authorization, token);
}
private void WirelessAdapter_NetworksChanged()
private void NetworkAdapter_Changed()
{
const int NOT_CONNECTED = -1;
try
{
var network = wirelessAdapter.GetNetworks().FirstOrDefault(n => n.Status == WirelessNetworkStatus.Connected);
var network = networkAdapter.GetWirelessNetworks().FirstOrDefault(n => n.Status == ConnectionStatus.Connected);
if (network?.SignalStrength != currentWlanValue)
{

View file

@ -30,7 +30,7 @@ namespace SafeExamBrowser.Settings.UserInterface
/// Determines whether the application log is accessible via the action center.
/// </summary>
public bool ShowApplicationLog { get; set; }
/// <summary>
/// Determines whether the system control for audio is accessible via the action center.
/// </summary>
@ -47,8 +47,8 @@ namespace SafeExamBrowser.Settings.UserInterface
public bool ShowKeyboardLayout { get; set; }
/// <summary>
/// Determines whether the system control for the wireless network is accessible via the action center.
/// Determines whether the system control for the network is accessible via the action center.
/// </summary>
public bool ShowWirelessNetwork { get; set; }
public bool ShowNetwork { get; set; }
}
}

View file

@ -47,8 +47,8 @@ namespace SafeExamBrowser.Settings.UserInterface
public bool ShowKeyboardLayout { get; set; }
/// <summary>
/// Determines whether the system control for the wireless network is accessible via the taskbar.
/// Determines whether the system control for the network is accessible via the taskbar.
/// </summary>
public bool ShowWirelessNetwork { get; set; }
public bool ShowNetwork { get; set; }
}
}

View file

@ -6,13 +6,16 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
namespace SafeExamBrowser.SystemComponents.Contracts.WirelessNetwork
namespace SafeExamBrowser.SystemComponents.Contracts.Network
{
/// <summary>
/// Defines all possible wireless network statuses which can be determined by the application.
/// Defines all possible connection statuses which can be determined by the application.
/// </summary>
public enum WirelessNetworkStatus
public enum ConnectionStatus
{
/// <summary>
/// The connection status is not determinable.
/// </summary>
Undefined = 0,
/// <summary>

View file

@ -0,0 +1,31 @@
/*
* Copyright (c) 2022 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.Network
{
/// <summary>
/// Defines all possible connection types which can be determined by the application.
/// </summary>
public enum ConnectionType
{
/// <summary>
/// The connection type cannot be determined.
/// </summary>
Undefined = 0,
/// <summary>
/// A wired network connection.
/// </summary>
Wired,
/// <summary>
/// A wireless network connection.
/// </summary>
Wireless
}
}

View file

@ -6,10 +6,10 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
namespace SafeExamBrowser.SystemComponents.Contracts.WirelessNetwork.Events
namespace SafeExamBrowser.SystemComponents.Contracts.Network.Events
{
/// <summary>
/// Indicates that the available wireless networks have changed.
/// Indicates that the network adapter has changed.
/// </summary>
public delegate void NetworksChangedEventHandler();
public delegate void ChangedEventHandler();
}

View file

@ -0,0 +1,45 @@
/*
* Copyright (c) 2022 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
using System;
using System.Collections.Generic;
using SafeExamBrowser.SystemComponents.Contracts.Network.Events;
namespace SafeExamBrowser.SystemComponents.Contracts.Network
{
/// <summary>
/// Defines the functionality of the network adapter system component.
/// </summary>
public interface INetworkAdapter : ISystemComponent
{
/// <summary>
/// The connection status of the network adapter.
/// </summary>
ConnectionStatus Status { get; }
/// <summary>
/// The type of the current network connection.
/// </summary>
ConnectionType Type { get; }
/// <summary>
/// Fired when the network adapter has changed.
/// </summary>
event ChangedEventHandler Changed;
/// <summary>
/// Attempts to connect to the wireless network with the given ID.
/// </summary>
void ConnectToWirelessNetwork(Guid id);
/// <summary>
/// Retrieves all currently available wireless networks.
/// </summary>
IEnumerable<IWirelessNetwork> GetWirelessNetworks();
}
}

View file

@ -8,7 +8,7 @@
using System;
namespace SafeExamBrowser.SystemComponents.Contracts.WirelessNetwork
namespace SafeExamBrowser.SystemComponents.Contracts.Network
{
/// <summary>
/// Defines a wireless network which can be connected to by the application.
@ -33,6 +33,6 @@ namespace SafeExamBrowser.SystemComponents.Contracts.WirelessNetwork
/// <summary>
/// The connection status of this network.
/// </summary>
WirelessNetworkStatus Status { get; }
ConnectionStatus Status { get; }
}
}

View file

@ -59,6 +59,7 @@
<Compile Include="IFileSystem.cs" />
<Compile Include="IRemoteSessionDetector.cs" />
<Compile Include="IVirtualMachineDetector.cs" />
<Compile Include="Network\ConnectionType.cs" />
<Compile Include="PowerSupply\Events\StatusChangedEventHandler.cs" />
<Compile Include="PowerSupply\IPowerSupply.cs" />
<Compile Include="PowerSupply\BatteryChargeStatus.cs" />
@ -68,14 +69,13 @@
<Compile Include="ISystemComponent.cs" />
<Compile Include="ISystemInfo.cs" />
<Compile Include="IUserInfo.cs" />
<Compile Include="WirelessNetwork\Events\NetworksChangedEventHandler.cs" />
<Compile Include="WirelessNetwork\Events\StatusChangedEventHandler.cs" />
<Compile Include="WirelessNetwork\IWirelessAdapter.cs" />
<Compile Include="WirelessNetwork\IWirelessNetwork.cs" />
<Compile Include="Network\Events\ChangedEventHandler.cs" />
<Compile Include="Network\INetworkAdapter.cs" />
<Compile Include="Network\IWirelessNetwork.cs" />
<Compile Include="OperatingSystem.cs" />
<Compile Include="PowerSupply\IPowerSupplyStatus.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="WirelessNetwork\WirelessNetworkStatus.cs" />
<Compile Include="Network\ConnectionStatus.cs" />
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />

View file

@ -1,15 +0,0 @@
/*
* Copyright (c) 2022 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.WirelessNetwork.Events
{
/// <summary>
/// Indicates that the wireless network status has changed.
/// </summary>
public delegate void StatusChangedEventHandler(WirelessNetworkStatus status);
}

View file

@ -1,42 +0,0 @@
/*
* Copyright (c) 2022 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
using System;
using System.Collections.Generic;
using SafeExamBrowser.SystemComponents.Contracts.WirelessNetwork.Events;
namespace SafeExamBrowser.SystemComponents.Contracts.WirelessNetwork
{
public interface IWirelessAdapter : ISystemComponent
{
/// <summary>
/// Fired when the available wireless networks changed.
/// </summary>
event NetworksChangedEventHandler NetworksChanged;
/// <summary>
/// Fired when the wireless network status changed.
/// </summary>
event StatusChangedEventHandler StatusChanged;
/// <summary>
/// Indicates whether the system has an active wireless network adapter.
/// </summary>
bool IsAvailable { get; }
/// <summary>
/// Attempts to connect to the wireless network with the given ID.
/// </summary>
void Connect(Guid id);
/// <summary>
/// Retrieves all currently available networks.
/// </summary>
IEnumerable<IWirelessNetwork> GetNetworks();
}
}

View file

@ -0,0 +1,215 @@
/*
* Copyright (c) 2022 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.NetworkInformation;
using System.Timers;
using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.SystemComponents.Contracts.Network;
using SafeExamBrowser.SystemComponents.Contracts.Network.Events;
using SafeExamBrowser.WindowsApi.Contracts;
using SimpleWifi;
using SimpleWifi.Win32;
using SimpleWifi.Win32.Interop;
namespace SafeExamBrowser.SystemComponents.Network
{
public class NetworkAdapter : INetworkAdapter
{
private readonly object @lock = new object();
private readonly ILogger logger;
private readonly INativeMethods nativeMethods;
private readonly List<WirelessNetwork> wirelessNetworks;
private Timer timer;
private Wifi wifi;
public ConnectionStatus Status { get; private set; }
public ConnectionType Type { get; private set; }
public event ChangedEventHandler Changed;
public NetworkAdapter(ILogger logger, INativeMethods nativeMethods)
{
this.logger = logger;
this.nativeMethods = nativeMethods;
this.wirelessNetworks = new List<WirelessNetwork>();
}
public void ConnectToWirelessNetwork(Guid id)
{
lock (@lock)
{
var network = wirelessNetworks.FirstOrDefault(n => n.Id == id);
if (network != default)
{
try
{
var request = new AuthRequest(network.AccessPoint);
logger.Info($"Attempting to connect to '{network.Name}'...");
network.AccessPoint.ConnectAsync(request, false, (success) => AccessPoint_OnConnectCompleted(network.Name, success));
Status = ConnectionStatus.Connecting;
}
catch (Exception e)
{
logger.Error($"Failed to connect to wireless network '{network.Name}!'", e);
}
}
else
{
logger.Warn($"Could not find network with id '{id}'!");
}
}
Changed?.Invoke();
}
public IEnumerable<IWirelessNetwork> GetWirelessNetworks()
{
lock (@lock)
{
return new List<WirelessNetwork>(wirelessNetworks);
}
}
public void Initialize()
{
const int FIVE_SECONDS = 5000;
NetworkChange.NetworkAddressChanged += (o, args) => Update();
NetworkChange.NetworkAvailabilityChanged += (o, args) => Update();
wifi = new Wifi();
wifi.ConnectionStatusChanged += (o, args) => Update();
timer = new Timer(FIVE_SECONDS);
timer.Elapsed += (o, args) => Update();
timer.AutoReset = true;
timer.Start();
Update();
logger.Info("Started monitoring the network adapter.");
}
public void Terminate()
{
if (timer != null)
{
timer.Stop();
logger.Info("Stopped monitoring the network adapter.");
}
}
private void AccessPoint_OnConnectCompleted(string name, bool success)
{
lock (@lock)
{
// This handler seems to be called before the connection has been fully established, thus we don't yet set the status to connected...
Status = success ? ConnectionStatus.Connecting : ConnectionStatus.Disconnected;
}
if (success)
{
logger.Info($"Successfully connected to wireless network '{name}'.");
}
else
{
logger.Error($"Failed to connect to wireless network '{name}!'");
}
}
private void Update()
{
try
{
lock (@lock)
{
var hasInternet = nativeMethods.HasInternetConnection();
var hasWireless = !wifi.NoWifiAvailable && !IsTurnedOff();
var isConnecting = Status == ConnectionStatus.Connecting;
var previousStatus = Status;
wirelessNetworks.Clear();
if (hasWireless)
{
foreach (var accessPoint in wifi.GetAccessPoints())
{
// The user may only connect to an already configured or connected wireless network!
if (accessPoint.HasProfile || accessPoint.IsConnected)
{
wirelessNetworks.Add(ToWirelessNetwork(accessPoint));
}
}
}
Type = hasWireless ? ConnectionType.Wireless : (hasInternet ? ConnectionType.Wired : ConnectionType.Undefined);
Status = hasInternet ? ConnectionStatus.Connected : (hasWireless && isConnecting ? ConnectionStatus.Connecting : ConnectionStatus.Disconnected);
if (previousStatus != ConnectionStatus.Connected && Status == ConnectionStatus.Connected)
{
logger.Info("Connection established.");
}
if (previousStatus != ConnectionStatus.Disconnected && Status == ConnectionStatus.Disconnected)
{
logger.Info("Connection lost.");
}
}
}
catch (Exception e)
{
logger.Error("Failed to update network adapter!", e);
}
Changed?.Invoke();
}
private bool IsTurnedOff()
{
try
{
var client = new WlanClient();
foreach (var @interface in client.Interfaces)
{
foreach (var state in @interface.RadioState.PhyRadioState)
{
if (state.dot11SoftwareRadioState == Dot11RadioState.On && state.dot11HardwareRadioState == Dot11RadioState.On)
{
return false;
}
}
}
}
catch (Exception e)
{
logger.Error("Failed to determine the radio state of the wireless adapter(s)! Assuming it is (all are) turned off...", e);
}
return true;
}
private WirelessNetwork ToWirelessNetwork(AccessPoint accessPoint)
{
return new WirelessNetwork
{
AccessPoint = accessPoint,
Name = accessPoint.Name,
SignalStrength = Convert.ToInt32(accessPoint.SignalStrength),
Status = accessPoint.IsConnected ? ConnectionStatus.Connected : ConnectionStatus.Disconnected
};
}
}
}

View file

@ -7,10 +7,10 @@
*/
using System;
using SafeExamBrowser.SystemComponents.Contracts.WirelessNetwork;
using SafeExamBrowser.SystemComponents.Contracts.Network;
using SimpleWifi;
namespace SafeExamBrowser.SystemComponents.WirelessNetwork
namespace SafeExamBrowser.SystemComponents.Network
{
internal class WirelessNetwork : IWirelessNetwork
{
@ -19,7 +19,7 @@ namespace SafeExamBrowser.SystemComponents.WirelessNetwork
public Guid Id { get; }
public string Name { get; set; }
public int SignalStrength { get; set; }
public WirelessNetworkStatus Status { get; set; }
public ConnectionStatus Status { get; set; }
public WirelessNetwork()
{

View file

@ -101,8 +101,8 @@
<Compile Include="SystemInfo.cs" />
<Compile Include="UserInfo.cs" />
<Compile Include="VirtualMachineDetector.cs" />
<Compile Include="WirelessNetwork\WirelessAdapter.cs" />
<Compile Include="WirelessNetwork\WirelessNetwork.cs" />
<Compile Include="Network\NetworkAdapter.cs" />
<Compile Include="Network\WirelessNetwork.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\SafeExamBrowser.Logging.Contracts\SafeExamBrowser.Logging.Contracts.csproj">
@ -117,6 +117,10 @@
<Project>{903129c6-e236-493b-9ad6-c6a57f647a3a}</Project>
<Name>SafeExamBrowser.SystemComponents.Contracts</Name>
</ProjectReference>
<ProjectReference Include="..\SafeExamBrowser.WindowsApi.Contracts\SafeExamBrowser.WindowsApi.Contracts.csproj">
<Project>{7016f080-9aa5-41b2-a225-385ad877c171}</Project>
<Name>SafeExamBrowser.WindowsApi.Contracts</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="app.config" />

View file

@ -1,203 +0,0 @@
/*
* Copyright (c) 2022 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Timers;
using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.SystemComponents.Contracts.WirelessNetwork;
using SafeExamBrowser.SystemComponents.Contracts.WirelessNetwork.Events;
using SimpleWifi;
using SimpleWifi.Win32;
using SimpleWifi.Win32.Interop;
namespace SafeExamBrowser.SystemComponents.WirelessNetwork
{
public class WirelessAdapter : IWirelessAdapter
{
private readonly object @lock = new object();
private List<WirelessNetwork> networks;
private ILogger logger;
private Timer timer;
private Wifi wifi;
public bool IsAvailable { get; private set; }
public event NetworksChangedEventHandler NetworksChanged;
public event StatusChangedEventHandler StatusChanged;
public WirelessAdapter(ILogger logger)
{
this.logger = logger;
this.networks = new List<WirelessNetwork>();
}
public void Connect(Guid id)
{
lock (@lock)
{
var network = networks.FirstOrDefault(n => n.Id == id);
if (network != default(WirelessNetwork))
{
try
{
var request = new AuthRequest(network.AccessPoint);
logger.Info($"Attempting to connect to '{network.Name}'...");
network.AccessPoint.ConnectAsync(request, false, (success) => AccessPoint_OnConnectCompleted(network.Name, success));
StatusChanged?.Invoke(WirelessNetworkStatus.Connecting);
}
catch (Exception e)
{
logger.Error($"Failed to connect to wireless network '{network.Name}!'", e);
}
}
else
{
logger.Warn($"Could not find network with id '{id}'!");
}
}
}
public IEnumerable<IWirelessNetwork> GetNetworks()
{
lock (@lock)
{
return new List<WirelessNetwork>(networks);
}
}
public void Initialize()
{
const int FIVE_SECONDS = 5000;
wifi = new Wifi();
wifi.ConnectionStatusChanged += Wifi_ConnectionStatusChanged;
IsAvailable = !wifi.NoWifiAvailable && !IsTurnedOff();
if (IsAvailable)
{
UpdateAvailableNetworks();
timer = new Timer(FIVE_SECONDS);
timer.Elapsed += Timer_Elapsed;
timer.AutoReset = true;
timer.Start();
logger.Info("Started monitoring the wireless network adapter.");
}
else
{
logger.Info("Wireless networks cannot be monitored, as there is no hardware adapter available or it is turned off.");
}
}
public void Terminate()
{
if (timer != null)
{
timer.Stop();
logger.Info("Stopped monitoring the wireless network adapter.");
}
}
private void AccessPoint_OnConnectCompleted(string name, bool success)
{
if (success)
{
logger.Info($"Successfully connected to wireless network '{name}'.");
}
else
{
logger.Error($"Failed to connect to wireless network '{name}!'");
}
UpdateAvailableNetworks();
}
private void Timer_Elapsed(object sender, ElapsedEventArgs e)
{
UpdateAvailableNetworks();
}
private void Wifi_ConnectionStatusChanged(object sender, WifiStatusEventArgs e)
{
UpdateAvailableNetworks();
}
private bool IsTurnedOff()
{
try
{
var client = new WlanClient();
foreach (var @interface in client.Interfaces)
{
foreach (var state in @interface.RadioState.PhyRadioState)
{
if (state.dot11SoftwareRadioState == Dot11RadioState.On && state.dot11HardwareRadioState == Dot11RadioState.On)
{
return false;
}
}
}
}
catch (Exception e)
{
logger.Error("Failed to determine the radio state of the wireless adapter(s)! Assuming it is (all are) turned off...", e);
}
return true;
}
private void UpdateAvailableNetworks()
{
lock (@lock)
{
try
{
networks.Clear();
foreach (var accessPoint in wifi.GetAccessPoints())
{
// The user may only connect to an already configured or connected wireless network!
if (accessPoint.HasProfile || accessPoint.IsConnected)
{
networks.Add(ToNetwork(accessPoint));
}
}
NetworksChanged?.Invoke();
}
catch (Exception e)
{
logger.Error("Failed to update available networks!", e);
}
}
}
private WirelessNetwork ToNetwork(AccessPoint accessPoint)
{
return new WirelessNetwork
{
AccessPoint = accessPoint,
Name = accessPoint.Name,
SignalStrength = Convert.ToInt32(accessPoint.SignalStrength),
Status = accessPoint.IsConnected ? WirelessNetworkStatus.Connected : WirelessNetworkStatus.Disconnected
};
}
private WirelessNetworkStatus ToStatus(WifiStatus status)
{
return status == WifiStatus.Connected ? WirelessNetworkStatus.Connected : WirelessNetworkStatus.Disconnected;
}
}
}

View file

@ -18,8 +18,8 @@ using SafeExamBrowser.Settings.Browser;
using SafeExamBrowser.Settings.Proctoring;
using SafeExamBrowser.SystemComponents.Contracts.Audio;
using SafeExamBrowser.SystemComponents.Contracts.Keyboard;
using SafeExamBrowser.SystemComponents.Contracts.Network;
using SafeExamBrowser.SystemComponents.Contracts.PowerSupply;
using SafeExamBrowser.SystemComponents.Contracts.WirelessNetwork;
using SafeExamBrowser.UserInterface.Contracts.Browser;
using SafeExamBrowser.UserInterface.Contracts.Proctoring;
using SafeExamBrowser.UserInterface.Contracts.Shell;
@ -78,6 +78,11 @@ namespace SafeExamBrowser.UserInterface.Contracts
/// </summary>
IWindow CreateLogWindow(ILogger logger);
/// <summary>
/// Creates a system control which allows to view and/or change the network connection of the computer.
/// </summary>
ISystemControl CreateNetworkControl(INetworkAdapter adapter, Location location);
/// <summary>
/// Creates a notification control for the given notification, initialized for the specified location.
/// </summary>
@ -132,10 +137,5 @@ namespace SafeExamBrowser.UserInterface.Contracts
/// Creates a new taskview.
/// </summary>
ITaskview CreateTaskview();
/// <summary>
/// Creates a system control which allows to change the wireless network connection of the computer.
/// </summary>
ISystemControl CreateWirelessNetworkControl(IWirelessAdapter wirelessAdapter, Location location);
}
}

View file

@ -1,4 +1,4 @@
<UserControl x:Class="SafeExamBrowser.UserInterface.Desktop.Controls.ActionCenter.WirelessNetworkButton" x:ClassModifier="internal"
<UserControl x:Class="SafeExamBrowser.UserInterface.Desktop.Controls.ActionCenter.NetworkButton" x:ClassModifier="internal"
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

@ -9,17 +9,17 @@
using System;
using System.Windows;
using System.Windows.Controls;
using SafeExamBrowser.SystemComponents.Contracts.WirelessNetwork;
using SafeExamBrowser.SystemComponents.Contracts.Network;
namespace SafeExamBrowser.UserInterface.Desktop.Controls.ActionCenter
{
internal partial class WirelessNetworkButton : UserControl
internal partial class NetworkButton : UserControl
{
private IWirelessNetwork network;
private readonly IWirelessNetwork network;
internal event EventHandler NetworkSelected;
internal WirelessNetworkButton(IWirelessNetwork network)
internal NetworkButton(IWirelessNetwork network)
{
this.network = network;
@ -30,7 +30,7 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls.ActionCenter
private void InitializeNetworkButton()
{
Button.Click += (o, args) => NetworkSelected?.Invoke(this, EventArgs.Empty);
IsCurrentTextBlock.Visibility = network.Status == WirelessNetworkStatus.Connected ? Visibility.Visible : Visibility.Hidden;
IsCurrentTextBlock.Visibility = network.Status == ConnectionStatus.Connected ? Visibility.Visible : Visibility.Hidden;
NetworkNameTextBlock.Text = network.Name;
SignalStrengthTextBlock.Text = $"{network.SignalStrength}%";
}

View file

@ -1,4 +1,4 @@
<UserControl x:Class="SafeExamBrowser.UserInterface.Desktop.Controls.ActionCenter.WirelessNetworkControl" x:ClassModifier="internal"
<UserControl x:Class="SafeExamBrowser.UserInterface.Desktop.Controls.ActionCenter.NetworkControl" x:ClassModifier="internal"
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"
@ -19,7 +19,7 @@
<Popup x:Name="Popup" IsOpen="False" Placement="Top" PlacementTarget="{Binding ElementName=Button}">
<Border Background="Gray">
<ScrollViewer MaxHeight="250" VerticalScrollBarVisibility="Auto" Template="{StaticResource SmallBarScrollViewer}">
<StackPanel x:Name="NetworksStackPanel" />
<StackPanel x:Name="WirelessNetworksStackPanel" />
</ScrollViewer>
</Border>
</Popup>
@ -30,10 +30,11 @@
<RowDefinition Height="3*" />
</Grid.RowDefinitions>
<Grid Grid.Row="0" HorizontalAlignment="Center" VerticalAlignment="Center">
<Viewbox x:Name="SignalStrengthIcon" Stretch="Uniform" Width="Auto" />
<fa:ImageAwesome x:Name="NoAdapterIcon" Foreground="Red" Icon="Ban" Margin="1" Opacity="0.3" Visibility="Collapsed" />
<fa:ImageAwesome x:Name="LoadingIcon" Foreground="Black" Icon="Cog" Margin="5" Spin="True" SpinDuration="2" Visibility="Collapsed" />
<Image x:Name="NetworkStatusIcon" Height="7" HorizontalAlignment="Right" Margin="-2,0" Panel.ZIndex="10" VerticalAlignment="Bottom" />
<Viewbox x:Name="WirelessIcon" Stretch="Uniform" Width="Auto" Visibility="Collapsed" />
<fa:ImageAwesome x:Name="WiredIcon" Icon="Tv" Margin="0,2,4,4" Visibility="Collapsed" />
<Border Background="White" CornerRadius="6" Height="12" HorizontalAlignment="Right" Margin="-3,0" Panel.ZIndex="10" VerticalAlignment="Bottom">
<fa:ImageAwesome x:Name="NetworkStatusIcon" />
</Border>
</Grid>
<TextBlock Grid.Row="1" x:Name="Text" FontSize="11" Foreground="White" TextAlignment="Center" TextTrimming="CharacterEllipsis" TextWrapping="Wrap" VerticalAlignment="Bottom" />
</Grid>

View file

@ -0,0 +1,135 @@
/*
* Copyright (c) 2022 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
using System;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using FontAwesome.WPF;
using SafeExamBrowser.Core.Contracts.Resources.Icons;
using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.SystemComponents.Contracts.Network;
using SafeExamBrowser.UserInterface.Contracts.Shell;
using SafeExamBrowser.UserInterface.Shared.Utilities;
namespace SafeExamBrowser.UserInterface.Desktop.Controls.ActionCenter
{
internal partial class NetworkControl : UserControl, ISystemControl
{
private readonly INetworkAdapter adapter;
private readonly IText text;
internal NetworkControl(INetworkAdapter adapter, IText text)
{
this.adapter = adapter;
this.text = text;
InitializeComponent();
InitializeWirelessNetworkControl();
}
public void Close()
{
Dispatcher.InvokeAsync(() => Popup.IsOpen = false);
}
private void InitializeWirelessNetworkControl()
{
var originalBrush = Grid.Background;
adapter.Changed += () => Dispatcher.InvokeAsync(Update);
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;
WirelessIcon.Child = GetWirelessIcon(0);
Update();
}
private void Update()
{
WirelessNetworksStackPanel.Children.Clear();
foreach (var network in adapter.GetWirelessNetworks())
{
var button = new NetworkButton(network);
button.NetworkSelected += (o, args) => adapter.ConnectToWirelessNetwork(network.Id);
if (network.Status == ConnectionStatus.Connected)
{
WirelessIcon.Child = GetWirelessIcon(network.SignalStrength);
UpdateText(text.Get(TextKey.SystemControl_NetworkWirelessConnected).Replace("%%NAME%%", network.Name));
}
WirelessNetworksStackPanel.Children.Add(button);
}
switch (adapter.Type)
{
case ConnectionType.Wired:
Button.IsEnabled = false;
UpdateText(text.Get(TextKey.SystemControl_NetworkWiredConnected));
WiredIcon.Visibility = Visibility.Visible;
WirelessIcon.Visibility = Visibility.Collapsed;
break;
case ConnectionType.Wireless:
Button.IsEnabled = true;
WiredIcon.Visibility = Visibility.Collapsed;
WirelessIcon.Visibility = Visibility.Visible;
break;
default:
Button.IsEnabled = false;
UpdateText(text.Get(TextKey.SystemControl_NetworkNotAvailable));
WiredIcon.Visibility = Visibility.Visible;
WirelessIcon.Visibility = Visibility.Collapsed;
break;
}
switch (adapter.Status)
{
case ConnectionStatus.Connected:
NetworkStatusIcon.Rotation = 0;
NetworkStatusIcon.Source = ImageAwesome.CreateImageSource(FontAwesomeIcon.Globe, Brushes.Green);
NetworkStatusIcon.Spin = false;
break;
case ConnectionStatus.Connecting:
UpdateText(text.Get(TextKey.SystemControl_NetworkWirelessConnecting));
NetworkStatusIcon.Rotation = 0;
NetworkStatusIcon.Source = ImageAwesome.CreateImageSource(FontAwesomeIcon.Cog, Brushes.DimGray);
NetworkStatusIcon.Spin = true;
NetworkStatusIcon.SpinDuration = 2;
break;
default:
UpdateText(text.Get(TextKey.SystemControl_NetworkDisconnected));
NetworkStatusIcon.Source = ImageAwesome.CreateImageSource(FontAwesomeIcon.Ban, Brushes.DarkOrange);
NetworkStatusIcon.Spin = false;
WirelessIcon.Child = GetWirelessIcon(0);
break;
}
}
private void UpdateText(string text)
{
Button.ToolTip = text;
Text.Text = text;
}
private UIElement GetWirelessIcon(int signalStrength)
{
var icon = signalStrength > 66 ? "100" : (signalStrength > 33 ? "66" : (signalStrength > 0 ? "33" : "0"));
var uri = new Uri($"pack://application:,,,/SafeExamBrowser.UserInterface.Desktop;component/Images/WiFi_Light_{icon}.xaml");
var resource = new XamlIconResource { Uri = uri };
return IconResourceLoader.Load(resource);
}
}
}

View file

@ -1,142 +0,0 @@
/*
* Copyright (c) 2022 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
using System;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using FontAwesome.WPF;
using SafeExamBrowser.Core.Contracts.Resources.Icons;
using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.SystemComponents.Contracts.WirelessNetwork;
using SafeExamBrowser.UserInterface.Contracts.Shell;
using SafeExamBrowser.UserInterface.Shared.Utilities;
namespace SafeExamBrowser.UserInterface.Desktop.Controls.ActionCenter
{
internal partial class WirelessNetworkControl : UserControl, ISystemControl
{
private IWirelessAdapter wirelessAdapter;
private IText text;
internal WirelessNetworkControl(IWirelessAdapter wirelessAdapter, IText text)
{
this.wirelessAdapter = wirelessAdapter;
this.text = text;
InitializeComponent();
InitializeWirelessNetworkControl();
}
public void Close()
{
Dispatcher.InvokeAsync(() => Popup.IsOpen = false);
}
private void InitializeWirelessNetworkControl()
{
var originalBrush = Grid.Background;
SignalStrengthIcon.Child = GetIcon(0);
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;
if (wirelessAdapter.IsAvailable)
{
wirelessAdapter.NetworksChanged += WirelessAdapter_NetworksChanged;
wirelessAdapter.StatusChanged += WirelessAdapter_StatusChanged;
UpdateNetworks();
}
else
{
Button.IsEnabled = false;
NoAdapterIcon.Visibility = Visibility.Visible;
UpdateText(text.Get(TextKey.SystemControl_WirelessNotAvailable));
}
}
private void WirelessAdapter_NetworksChanged()
{
Dispatcher.InvokeAsync(UpdateNetworks);
}
private void WirelessAdapter_StatusChanged(WirelessNetworkStatus status)
{
Dispatcher.InvokeAsync(() => UpdateStatus(status));
}
private void UpdateNetworks()
{
var status = WirelessNetworkStatus.Disconnected;
NetworksStackPanel.Children.Clear();
foreach (var network in wirelessAdapter.GetNetworks())
{
var button = new WirelessNetworkButton(network);
button.NetworkSelected += (o, args) => wirelessAdapter.Connect(network.Id);
if (network.Status == WirelessNetworkStatus.Connected)
{
status = WirelessNetworkStatus.Connected;
SignalStrengthIcon.Child = GetIcon(network.SignalStrength);
UpdateText(text.Get(TextKey.SystemControl_WirelessConnected).Replace("%%NAME%%", network.Name));
}
NetworksStackPanel.Children.Add(button);
}
UpdateStatus(status);
}
private void UpdateStatus(WirelessNetworkStatus status)
{
LoadingIcon.Visibility = Visibility.Collapsed;
SignalStrengthIcon.Visibility = Visibility.Visible;
NetworkStatusIcon.Visibility = Visibility.Visible;
switch (status)
{
case WirelessNetworkStatus.Connected:
NetworkStatusIcon.Source = ImageAwesome.CreateImageSource(FontAwesomeIcon.Check, Brushes.Green);
break;
case WirelessNetworkStatus.Connecting:
LoadingIcon.Visibility = Visibility.Visible;
SignalStrengthIcon.Visibility = Visibility.Collapsed;
NetworkStatusIcon.Visibility = Visibility.Collapsed;
UpdateText(text.Get(TextKey.SystemControl_WirelessConnecting));
break;
default:
NetworkStatusIcon.Source = ImageAwesome.CreateImageSource(FontAwesomeIcon.Close, Brushes.Orange);
SignalStrengthIcon.Child = GetIcon(0);
UpdateText(text.Get(TextKey.SystemControl_WirelessDisconnected));
break;
}
}
private void UpdateText(string text)
{
Button.ToolTip = text;
Text.Text = text;
}
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.Desktop;component/Images/WiFi_Light_{icon}.xaml");
var resource = new XamlIconResource { Uri = uri };
return IconResourceLoader.Load(resource);
}
}
}

View file

@ -1,4 +1,4 @@
<UserControl x:Class="SafeExamBrowser.UserInterface.Desktop.Controls.Taskbar.WirelessNetworkButton" x:ClassModifier="internal"
<UserControl x:Class="SafeExamBrowser.UserInterface.Desktop.Controls.Taskbar.NetworkButton" x:ClassModifier="internal"
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

@ -9,17 +9,17 @@
using System;
using System.Windows;
using System.Windows.Controls;
using SafeExamBrowser.SystemComponents.Contracts.WirelessNetwork;
using SafeExamBrowser.SystemComponents.Contracts.Network;
namespace SafeExamBrowser.UserInterface.Desktop.Controls.Taskbar
{
internal partial class WirelessNetworkButton : UserControl
internal partial class NetworkButton : UserControl
{
private IWirelessNetwork network;
private readonly IWirelessNetwork network;
internal event EventHandler NetworkSelected;
internal WirelessNetworkButton(IWirelessNetwork network)
internal NetworkButton(IWirelessNetwork network)
{
this.network = network;
@ -30,7 +30,7 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls.Taskbar
private void InitializeNetworkButton()
{
Button.Click += (o, args) => NetworkSelected?.Invoke(this, EventArgs.Empty);
IsCurrentTextBlock.Visibility = network.Status == WirelessNetworkStatus.Connected ? Visibility.Visible : Visibility.Hidden;
IsCurrentTextBlock.Visibility = network.Status == ConnectionStatus.Connected ? Visibility.Visible : Visibility.Hidden;
NetworkNameTextBlock.Text = network.Name;
SignalStrengthTextBlock.Text = $"{network.SignalStrength}%";
}

View file

@ -1,4 +1,4 @@
<UserControl x:Class="SafeExamBrowser.UserInterface.Desktop.Controls.Taskbar.WirelessNetworkControl" x:ClassModifier="internal"
<UserControl x:Class="SafeExamBrowser.UserInterface.Desktop.Controls.Taskbar.NetworkControl" x:ClassModifier="internal"
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"
@ -20,16 +20,17 @@
<Popup x:Name="Popup" IsOpen="False" Placement="Custom" PlacementTarget="{Binding ElementName=Button}">
<Border Background="LightGray" BorderBrush="Gray" BorderThickness="1,1,1,0">
<ScrollViewer MaxHeight="250" VerticalScrollBarVisibility="Auto" Template="{StaticResource SmallBarScrollViewer}">
<StackPanel x:Name="NetworksStackPanel" />
<StackPanel x:Name="WirelessNetworksStackPanel" />
</ScrollViewer>
</Border>
</Popup>
<Button x:Name="Button" Background="Transparent" Padding="5" Template="{StaticResource TaskbarButton}" ToolTipService.ShowOnDisabled="True" Width="40">
<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
<Viewbox x:Name="SignalStrengthIcon" Stretch="Uniform" Width="Auto" />
<fa:ImageAwesome x:Name="NoAdapterIcon" Foreground="Red" Icon="Ban" Margin="1" Opacity="0.3" Visibility="Collapsed" />
<fa:ImageAwesome x:Name="LoadingIcon" Foreground="Gray" Icon="Cog" Margin="5" Spin="True" SpinDuration="2" Visibility="Collapsed" />
<Image x:Name="NetworkStatusIcon" Height="7" HorizontalAlignment="Right" Margin="0,2" Panel.ZIndex="10" VerticalAlignment="Bottom" />
<Viewbox x:Name="WirelessIcon" Stretch="Uniform" Width="Auto" Visibility="Collapsed" />
<fa:ImageAwesome x:Name="WiredIcon" Icon="Tv" Margin="0,2,4,4" Visibility="Collapsed" />
<Border Background="{StaticResource BackgroundBrush}" CornerRadius="6" Height="12" HorizontalAlignment="Right" Margin="0,0,-1,1" Panel.ZIndex="10" VerticalAlignment="Bottom">
<fa:ImageAwesome x:Name="NetworkStatusIcon" />
</Border>
</Grid>
</Button>
</Grid>

View file

@ -15,20 +15,20 @@ using System.Windows.Media;
using FontAwesome.WPF;
using SafeExamBrowser.Core.Contracts.Resources.Icons;
using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.SystemComponents.Contracts.WirelessNetwork;
using SafeExamBrowser.SystemComponents.Contracts.Network;
using SafeExamBrowser.UserInterface.Contracts.Shell;
using SafeExamBrowser.UserInterface.Shared.Utilities;
namespace SafeExamBrowser.UserInterface.Desktop.Controls.Taskbar
{
internal partial class WirelessNetworkControl : UserControl, ISystemControl
internal partial class NetworkControl : UserControl, ISystemControl
{
private IWirelessAdapter wirelessAdapter;
private IText text;
private readonly INetworkAdapter adapter;
private readonly IText text;
internal WirelessNetworkControl(IWirelessAdapter wirelessAdapter, IText text)
internal NetworkControl(INetworkAdapter adapter, IText text)
{
this.wirelessAdapter = wirelessAdapter;
this.adapter = adapter;
this.text = text;
InitializeComponent();
@ -44,11 +44,12 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls.Taskbar
{
var originalBrush = Button.Background;
SignalStrengthIcon.Child = GetIcon(0);
adapter.Changed += () => Dispatcher.InvokeAsync(Update);
Button.Click += (o, args) => Popup.IsOpen = !Popup.IsOpen;
Button.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => Popup.IsOpen = Popup.IsMouseOver));
Popup.CustomPopupPlacementCallback = new CustomPopupPlacementCallback(Popup_PlacementCallback);
Popup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => Popup.IsOpen = IsMouseOver));
WirelessIcon.Child = GetWirelessIcon(0);
Popup.Opened += (o, args) =>
{
@ -62,18 +63,7 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls.Taskbar
Button.Background = originalBrush;
};
if (wirelessAdapter.IsAvailable)
{
wirelessAdapter.NetworksChanged += WirelessAdapter_NetworksChanged;
wirelessAdapter.StatusChanged += WirelessAdapter_StatusChanged;
UpdateNetworks();
}
else
{
Button.IsEnabled = false;
NoAdapterIcon.Visibility = Visibility.Visible;
UpdateText(text.Get(TextKey.SystemControl_WirelessNotAvailable));
}
Update();
}
private CustomPopupPlacement[] Popup_PlacementCallback(Size popupSize, Size targetSize, Point offset)
@ -84,72 +74,70 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls.Taskbar
};
}
private void WirelessAdapter_NetworksChanged()
private void Update()
{
Dispatcher.InvokeAsync(UpdateNetworks);
}
WirelessNetworksStackPanel.Children.Clear();
private void WirelessAdapter_StatusChanged(WirelessNetworkStatus status)
{
Dispatcher.InvokeAsync(() => UpdateStatus(status));
}
private void UpdateNetworks()
{
var status = WirelessNetworkStatus.Disconnected;
NetworksStackPanel.Children.Clear();
foreach (var network in wirelessAdapter.GetNetworks())
foreach (var network in adapter.GetWirelessNetworks())
{
var button = new WirelessNetworkButton(network);
var button = new NetworkButton(network);
button.NetworkSelected += (o, args) => wirelessAdapter.Connect(network.Id);
button.NetworkSelected += (o, args) => adapter.ConnectToWirelessNetwork(network.Id);
if (network.Status == WirelessNetworkStatus.Connected)
if (network.Status == ConnectionStatus.Connected)
{
status = WirelessNetworkStatus.Connected;
SignalStrengthIcon.Child = GetIcon(network.SignalStrength);
UpdateText(text.Get(TextKey.SystemControl_WirelessConnected).Replace("%%NAME%%", network.Name));
WirelessIcon.Child = GetWirelessIcon(network.SignalStrength);
Button.ToolTip = text.Get(TextKey.SystemControl_NetworkWirelessConnected).Replace("%%NAME%%", network.Name);
}
NetworksStackPanel.Children.Add(button);
WirelessNetworksStackPanel.Children.Add(button);
}
UpdateStatus(status);
}
private void UpdateStatus(WirelessNetworkStatus status)
{
LoadingIcon.Visibility = Visibility.Collapsed;
SignalStrengthIcon.Visibility = Visibility.Visible;
NetworkStatusIcon.Visibility = Visibility.Visible;
switch (status)
switch (adapter.Type)
{
case WirelessNetworkStatus.Connected:
NetworkStatusIcon.Source = ImageAwesome.CreateImageSource(FontAwesomeIcon.Check, Brushes.Green);
case ConnectionType.Wired:
Button.IsEnabled = false;
Button.ToolTip = text.Get(TextKey.SystemControl_NetworkWiredConnected);
WiredIcon.Visibility = Visibility.Visible;
WirelessIcon.Visibility = Visibility.Collapsed;
break;
case WirelessNetworkStatus.Connecting:
LoadingIcon.Visibility = Visibility.Visible;
SignalStrengthIcon.Visibility = Visibility.Collapsed;
NetworkStatusIcon.Visibility = Visibility.Collapsed;
UpdateText(text.Get(TextKey.SystemControl_WirelessConnecting));
case ConnectionType.Wireless:
Button.IsEnabled = true;
WiredIcon.Visibility = Visibility.Collapsed;
WirelessIcon.Visibility = Visibility.Visible;
break;
default:
NetworkStatusIcon.Source = ImageAwesome.CreateImageSource(FontAwesomeIcon.Close, Brushes.Orange);
SignalStrengthIcon.Child = GetIcon(0);
UpdateText(text.Get(TextKey.SystemControl_WirelessDisconnected));
Button.IsEnabled = false;
Button.ToolTip = text.Get(TextKey.SystemControl_NetworkNotAvailable);
WiredIcon.Visibility = Visibility.Visible;
WirelessIcon.Visibility = Visibility.Collapsed;
break;
}
switch (adapter.Status)
{
case ConnectionStatus.Connected:
NetworkStatusIcon.Rotation = 0;
NetworkStatusIcon.Source = ImageAwesome.CreateImageSource(FontAwesomeIcon.Globe, Brushes.Green);
NetworkStatusIcon.Spin = false;
break;
case ConnectionStatus.Connecting:
Button.ToolTip = text.Get(TextKey.SystemControl_NetworkWirelessConnecting);
NetworkStatusIcon.Rotation = 0;
NetworkStatusIcon.Source = ImageAwesome.CreateImageSource(FontAwesomeIcon.Cog, Brushes.DimGray);
NetworkStatusIcon.Spin = true;
NetworkStatusIcon.SpinDuration = 2;
break;
default:
Button.ToolTip = text.Get(TextKey.SystemControl_NetworkDisconnected);
NetworkStatusIcon.Source = ImageAwesome.CreateImageSource(FontAwesomeIcon.Ban, Brushes.DarkOrange);
NetworkStatusIcon.Spin = false;
WirelessIcon.Child = GetWirelessIcon(0);
break;
}
}
private void UpdateText(string text)
{
Button.ToolTip = text;
}
private UIElement GetIcon(int signalStrength)
private UIElement GetWirelessIcon(int signalStrength)
{
var icon = signalStrength > 66 ? "100" : (signalStrength > 33 ? "66" : (signalStrength > 0 ? "33" : "0"));
var uri = new Uri($"pack://application:,,,/SafeExamBrowser.UserInterface.Desktop;component/Images/WiFi_{icon}.xaml");

View file

@ -109,11 +109,11 @@
<Compile Include="Controls\ActionCenter\QuitButton.xaml.cs">
<DependentUpon>QuitButton.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\ActionCenter\WirelessNetworkButton.xaml.cs">
<DependentUpon>WirelessNetworkButton.xaml</DependentUpon>
<Compile Include="Controls\ActionCenter\NetworkButton.xaml.cs">
<DependentUpon>NetworkButton.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\ActionCenter\WirelessNetworkControl.xaml.cs">
<DependentUpon>WirelessNetworkControl.xaml</DependentUpon>
<Compile Include="Controls\ActionCenter\NetworkControl.xaml.cs">
<DependentUpon>NetworkControl.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\Browser\DownloadItemControl.xaml.cs">
<DependentUpon>DownloadItemControl.xaml</DependentUpon>
@ -145,11 +145,11 @@
<Compile Include="Controls\Taskbar\QuitButton.xaml.cs">
<DependentUpon>QuitButton.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\Taskbar\WirelessNetworkButton.xaml.cs">
<DependentUpon>WirelessNetworkButton.xaml</DependentUpon>
<Compile Include="Controls\Taskbar\NetworkButton.xaml.cs">
<DependentUpon>NetworkButton.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\Taskbar\WirelessNetworkControl.xaml.cs">
<DependentUpon>WirelessNetworkControl.xaml</DependentUpon>
<Compile Include="Controls\Taskbar\NetworkControl.xaml.cs">
<DependentUpon>NetworkControl.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\Taskview\WindowControl.xaml.cs">
<DependentUpon>WindowControl.xaml</DependentUpon>
@ -263,11 +263,11 @@
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Controls\ActionCenter\WirelessNetworkButton.xaml">
<Page Include="Controls\ActionCenter\NetworkButton.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Controls\ActionCenter\WirelessNetworkControl.xaml">
<Page Include="Controls\ActionCenter\NetworkControl.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
@ -451,11 +451,11 @@
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Resource>
<Page Include="Controls\Taskbar\WirelessNetworkButton.xaml">
<Page Include="Controls\Taskbar\NetworkButton.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Controls\Taskbar\WirelessNetworkControl.xaml">
<Page Include="Controls\Taskbar\NetworkControl.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>

View file

@ -22,8 +22,8 @@ using SafeExamBrowser.Settings.Browser;
using SafeExamBrowser.Settings.Proctoring;
using SafeExamBrowser.SystemComponents.Contracts.Audio;
using SafeExamBrowser.SystemComponents.Contracts.Keyboard;
using SafeExamBrowser.SystemComponents.Contracts.Network;
using SafeExamBrowser.SystemComponents.Contracts.PowerSupply;
using SafeExamBrowser.SystemComponents.Contracts.WirelessNetwork;
using SafeExamBrowser.UserInterface.Contracts;
using SafeExamBrowser.UserInterface.Contracts.Browser;
using SafeExamBrowser.UserInterface.Contracts.Proctoring;
@ -37,7 +37,7 @@ namespace SafeExamBrowser.UserInterface.Desktop
{
public class UserInterfaceFactory : IUserInterfaceFactory
{
private IText text;
private readonly IText text;
public UserInterfaceFactory(IText text)
{
@ -131,6 +131,18 @@ namespace SafeExamBrowser.UserInterface.Desktop
return window;
}
public ISystemControl CreateNetworkControl(INetworkAdapter adapter, Location location)
{
if (location == Location.ActionCenter)
{
return new Controls.ActionCenter.NetworkControl(adapter, text);
}
else
{
return new Controls.Taskbar.NetworkControl(adapter, text);
}
}
public INotificationControl CreateNotificationControl(INotification notification, Location location)
{
if (location == Location.ActionCenter)
@ -226,18 +238,6 @@ namespace SafeExamBrowser.UserInterface.Desktop
return new Taskview();
}
public ISystemControl CreateWirelessNetworkControl(IWirelessAdapter wirelessAdapter, Location location)
{
if (location == Location.ActionCenter)
{
return new Controls.ActionCenter.WirelessNetworkControl(wirelessAdapter, text);
}
else
{
return new Controls.Taskbar.WirelessNetworkControl(wirelessAdapter, text);
}
}
private void InitializeFontAwesome()
{
// To be able to use FontAwesome in XAML icon resources, we need to make sure that the FontAwesome.WPF assembly is loaded into

View file

@ -1,4 +1,4 @@
<UserControl x:Class="SafeExamBrowser.UserInterface.Mobile.Controls.ActionCenter.WirelessNetworkButton" x:ClassModifier="internal"
<UserControl x:Class="SafeExamBrowser.UserInterface.Mobile.Controls.ActionCenter.NetworkButton" x:ClassModifier="internal"
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

@ -9,17 +9,17 @@
using System;
using System.Windows;
using System.Windows.Controls;
using SafeExamBrowser.SystemComponents.Contracts.WirelessNetwork;
using SafeExamBrowser.SystemComponents.Contracts.Network;
namespace SafeExamBrowser.UserInterface.Mobile.Controls.ActionCenter
{
internal partial class WirelessNetworkButton : UserControl
internal partial class NetworkButton : UserControl
{
private IWirelessNetwork network;
private readonly IWirelessNetwork network;
internal event EventHandler NetworkSelected;
internal WirelessNetworkButton(IWirelessNetwork network)
internal NetworkButton(IWirelessNetwork network)
{
this.network = network;
@ -30,7 +30,7 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls.ActionCenter
private void InitializeNetworkButton()
{
Button.Click += (o, args) => NetworkSelected?.Invoke(this, EventArgs.Empty);
IsCurrentTextBlock.Visibility = network.Status == WirelessNetworkStatus.Connected ? Visibility.Visible : Visibility.Hidden;
IsCurrentTextBlock.Visibility = network.Status == ConnectionStatus.Connected ? Visibility.Visible : Visibility.Hidden;
NetworkNameTextBlock.Text = network.Name;
SignalStrengthTextBlock.Text = $"{network.SignalStrength}%";
}

View file

@ -1,4 +1,4 @@
<UserControl x:Class="SafeExamBrowser.UserInterface.Mobile.Controls.ActionCenter.WirelessNetworkControl" x:ClassModifier="internal"
<UserControl x:Class="SafeExamBrowser.UserInterface.Mobile.Controls.ActionCenter.NetworkControl" x:ClassModifier="internal"
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"
@ -19,7 +19,7 @@
<Popup x:Name="Popup" IsOpen="False" Placement="Top" PlacementTarget="{Binding ElementName=Button}">
<Border Background="Gray">
<ScrollViewer MaxHeight="250" VerticalScrollBarVisibility="Auto" Template="{StaticResource SmallBarScrollViewer}">
<StackPanel x:Name="NetworksStackPanel" />
<StackPanel x:Name="WirelessNetworksStackPanel" />
</ScrollViewer>
</Border>
</Popup>
@ -30,10 +30,11 @@
<RowDefinition Height="3*" />
</Grid.RowDefinitions>
<Grid Grid.Row="0" HorizontalAlignment="Center" VerticalAlignment="Center">
<Viewbox x:Name="SignalStrengthIcon" Stretch="Uniform" Width="Auto" />
<fa:ImageAwesome x:Name="NoAdapterIcon" Foreground="Red" Icon="Ban" Margin="1" Opacity="0.3" Visibility="Collapsed" />
<fa:ImageAwesome x:Name="LoadingIcon" Foreground="Black" Icon="Cog" Margin="5" Spin="True" SpinDuration="2" Visibility="Collapsed" />
<Image x:Name="NetworkStatusIcon" Height="8" HorizontalAlignment="Right" Margin="-2,0" Panel.ZIndex="10" VerticalAlignment="Bottom" />
<Viewbox x:Name="WirelessIcon" Stretch="Uniform" Width="Auto" Visibility="Collapsed" />
<fa:ImageAwesome x:Name="WiredIcon" Icon="Tv" Margin="0,2,4,4" Visibility="Collapsed" />
<Border Background="White" CornerRadius="6" Height="14" HorizontalAlignment="Right" Margin="-3,0" Panel.ZIndex="10" VerticalAlignment="Bottom">
<fa:ImageAwesome x:Name="NetworkStatusIcon" />
</Border>
</Grid>
<TextBlock Grid.Row="1" x:Name="Text" FontSize="15" Foreground="White" TextAlignment="Center" TextTrimming="CharacterEllipsis" TextWrapping="Wrap" VerticalAlignment="Bottom" />
</Grid>

View file

@ -0,0 +1,135 @@
/*
* Copyright (c) 2022 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
using System;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using FontAwesome.WPF;
using SafeExamBrowser.Core.Contracts.Resources.Icons;
using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.SystemComponents.Contracts.Network;
using SafeExamBrowser.UserInterface.Contracts.Shell;
using SafeExamBrowser.UserInterface.Shared.Utilities;
namespace SafeExamBrowser.UserInterface.Mobile.Controls.ActionCenter
{
internal partial class NetworkControl : UserControl, ISystemControl
{
private readonly INetworkAdapter adapter;
private readonly IText text;
internal NetworkControl(INetworkAdapter adapter, IText text)
{
this.adapter = adapter;
this.text = text;
InitializeComponent();
InitializeWirelessNetworkControl();
}
public void Close()
{
Dispatcher.InvokeAsync(() => Popup.IsOpen = false);
}
private void InitializeWirelessNetworkControl()
{
var originalBrush = Grid.Background;
adapter.Changed += () => Dispatcher.InvokeAsync(Update);
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;
WirelessIcon.Child = GetWirelessIcon(0);
Update();
}
private void Update()
{
WirelessNetworksStackPanel.Children.Clear();
foreach (var network in adapter.GetWirelessNetworks())
{
var button = new NetworkButton(network);
button.NetworkSelected += (o, args) => adapter.ConnectToWirelessNetwork(network.Id);
if (network.Status == ConnectionStatus.Connected)
{
WirelessIcon.Child = GetWirelessIcon(network.SignalStrength);
UpdateText(text.Get(TextKey.SystemControl_NetworkWirelessConnected).Replace("%%NAME%%", network.Name));
}
WirelessNetworksStackPanel.Children.Add(button);
}
switch (adapter.Type)
{
case ConnectionType.Wired:
Button.IsEnabled = false;
UpdateText(text.Get(TextKey.SystemControl_NetworkWiredConnected));
WiredIcon.Visibility = Visibility.Visible;
WirelessIcon.Visibility = Visibility.Collapsed;
break;
case ConnectionType.Wireless:
Button.IsEnabled = true;
WiredIcon.Visibility = Visibility.Collapsed;
WirelessIcon.Visibility = Visibility.Visible;
break;
default:
Button.IsEnabled = false;
UpdateText(text.Get(TextKey.SystemControl_NetworkNotAvailable));
WiredIcon.Visibility = Visibility.Visible;
WirelessIcon.Visibility = Visibility.Collapsed;
break;
}
switch (adapter.Status)
{
case ConnectionStatus.Connected:
NetworkStatusIcon.Rotation = 0;
NetworkStatusIcon.Source = ImageAwesome.CreateImageSource(FontAwesomeIcon.Globe, Brushes.Green);
NetworkStatusIcon.Spin = false;
break;
case ConnectionStatus.Connecting:
UpdateText(text.Get(TextKey.SystemControl_NetworkWirelessConnecting));
NetworkStatusIcon.Rotation = 0;
NetworkStatusIcon.Source = ImageAwesome.CreateImageSource(FontAwesomeIcon.Cog, Brushes.DimGray);
NetworkStatusIcon.Spin = true;
NetworkStatusIcon.SpinDuration = 2;
break;
default:
UpdateText(text.Get(TextKey.SystemControl_NetworkDisconnected));
NetworkStatusIcon.Source = ImageAwesome.CreateImageSource(FontAwesomeIcon.Ban, Brushes.DarkOrange);
NetworkStatusIcon.Spin = false;
WirelessIcon.Child = GetWirelessIcon(0);
break;
}
}
private void UpdateText(string text)
{
Button.ToolTip = text;
Text.Text = text;
}
private UIElement GetWirelessIcon(int signalStrength)
{
var icon = signalStrength > 66 ? "100" : (signalStrength > 33 ? "66" : (signalStrength > 0 ? "33" : "0"));
var uri = new Uri($"pack://application:,,,/SafeExamBrowser.UserInterface.Mobile;component/Images/WiFi_Light_{icon}.xaml");
var resource = new XamlIconResource { Uri = uri };
return IconResourceLoader.Load(resource);
}
}
}

View file

@ -1,142 +0,0 @@
/*
* Copyright (c) 2022 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
using System;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using FontAwesome.WPF;
using SafeExamBrowser.Core.Contracts.Resources.Icons;
using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.SystemComponents.Contracts.WirelessNetwork;
using SafeExamBrowser.UserInterface.Contracts.Shell;
using SafeExamBrowser.UserInterface.Shared.Utilities;
namespace SafeExamBrowser.UserInterface.Mobile.Controls.ActionCenter
{
internal partial class WirelessNetworkControl : UserControl, ISystemControl
{
private IWirelessAdapter wirelessAdapter;
private IText text;
internal WirelessNetworkControl(IWirelessAdapter wirelessAdapter, IText text)
{
this.wirelessAdapter = wirelessAdapter;
this.text = text;
InitializeComponent();
InitializeWirelessNetworkControl();
}
public void Close()
{
Dispatcher.InvokeAsync(() => Popup.IsOpen = false);
}
private void InitializeWirelessNetworkControl()
{
var originalBrush = Grid.Background;
SignalStrengthIcon.Child = GetIcon(0);
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;
if (wirelessAdapter.IsAvailable)
{
wirelessAdapter.NetworksChanged += WirelessAdapter_NetworksChanged;
wirelessAdapter.StatusChanged += WirelessAdapter_StatusChanged;
UpdateNetworks();
}
else
{
Button.IsEnabled = false;
NoAdapterIcon.Visibility = Visibility.Visible;
UpdateText(text.Get(TextKey.SystemControl_WirelessNotAvailable));
}
}
private void WirelessAdapter_NetworksChanged()
{
Dispatcher.InvokeAsync(UpdateNetworks);
}
private void WirelessAdapter_StatusChanged(WirelessNetworkStatus status)
{
Dispatcher.InvokeAsync(() => UpdateStatus(status));
}
private void UpdateNetworks()
{
var status = WirelessNetworkStatus.Disconnected;
NetworksStackPanel.Children.Clear();
foreach (var network in wirelessAdapter.GetNetworks())
{
var button = new WirelessNetworkButton(network);
button.NetworkSelected += (o, args) => wirelessAdapter.Connect(network.Id);
if (network.Status == WirelessNetworkStatus.Connected)
{
status = WirelessNetworkStatus.Connected;
SignalStrengthIcon.Child = GetIcon(network.SignalStrength);
UpdateText(text.Get(TextKey.SystemControl_WirelessConnected).Replace("%%NAME%%", network.Name));
}
NetworksStackPanel.Children.Add(button);
}
UpdateStatus(status);
}
private void UpdateStatus(WirelessNetworkStatus status)
{
LoadingIcon.Visibility = Visibility.Collapsed;
SignalStrengthIcon.Visibility = Visibility.Visible;
NetworkStatusIcon.Visibility = Visibility.Visible;
switch (status)
{
case WirelessNetworkStatus.Connected:
NetworkStatusIcon.Source = ImageAwesome.CreateImageSource(FontAwesomeIcon.Check, Brushes.Green);
break;
case WirelessNetworkStatus.Connecting:
LoadingIcon.Visibility = Visibility.Visible;
SignalStrengthIcon.Visibility = Visibility.Collapsed;
NetworkStatusIcon.Visibility = Visibility.Collapsed;
UpdateText(text.Get(TextKey.SystemControl_WirelessConnecting));
break;
default:
NetworkStatusIcon.Source = ImageAwesome.CreateImageSource(FontAwesomeIcon.Close, Brushes.Orange);
SignalStrengthIcon.Child = GetIcon(0);
UpdateText(text.Get(TextKey.SystemControl_WirelessDisconnected));
break;
}
}
private void UpdateText(string text)
{
Button.ToolTip = text;
Text.Text = text;
}
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.Mobile;component/Images/WiFi_Light_{icon}.xaml");
var resource = new XamlIconResource { Uri = uri };
return IconResourceLoader.Load(resource);
}
}
}

View file

@ -1,4 +1,4 @@
<UserControl x:Class="SafeExamBrowser.UserInterface.Mobile.Controls.Taskbar.WirelessNetworkButton" x:ClassModifier="internal"
<UserControl x:Class="SafeExamBrowser.UserInterface.Mobile.Controls.Taskbar.NetworkButton" x:ClassModifier="internal"
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

@ -9,17 +9,17 @@
using System;
using System.Windows;
using System.Windows.Controls;
using SafeExamBrowser.SystemComponents.Contracts.WirelessNetwork;
using SafeExamBrowser.SystemComponents.Contracts.Network;
namespace SafeExamBrowser.UserInterface.Mobile.Controls.Taskbar
{
internal partial class WirelessNetworkButton : UserControl
internal partial class NetworkButton : UserControl
{
private IWirelessNetwork network;
private readonly IWirelessNetwork network;
internal event EventHandler NetworkSelected;
internal WirelessNetworkButton(IWirelessNetwork network)
internal NetworkButton(IWirelessNetwork network)
{
this.network = network;
@ -30,7 +30,7 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls.Taskbar
private void InitializeNetworkButton()
{
Button.Click += (o, args) => NetworkSelected?.Invoke(this, EventArgs.Empty);
IsCurrentTextBlock.Visibility = network.Status == WirelessNetworkStatus.Connected ? Visibility.Visible : Visibility.Hidden;
IsCurrentTextBlock.Visibility = network.Status == ConnectionStatus.Connected ? Visibility.Visible : Visibility.Hidden;
NetworkNameTextBlock.Text = network.Name;
SignalStrengthTextBlock.Text = $"{network.SignalStrength}%";
}

View file

@ -1,4 +1,4 @@
<UserControl x:Class="SafeExamBrowser.UserInterface.Mobile.Controls.Taskbar.WirelessNetworkControl" x:ClassModifier="internal"
<UserControl x:Class="SafeExamBrowser.UserInterface.Mobile.Controls.Taskbar.NetworkControl" x:ClassModifier="internal"
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"
@ -20,16 +20,17 @@
<Popup x:Name="Popup" IsOpen="False" Placement="Custom" PlacementTarget="{Binding ElementName=Button}">
<Border Background="LightGray" BorderBrush="Gray" BorderThickness="1,1,1,0">
<ScrollViewer MaxHeight="250" VerticalScrollBarVisibility="Auto" Template="{StaticResource SmallBarScrollViewer}">
<StackPanel x:Name="NetworksStackPanel" />
<StackPanel x:Name="WirelessNetworksStackPanel" />
</ScrollViewer>
</Border>
</Popup>
<Button x:Name="Button" Background="Transparent" Padding="5" Template="{StaticResource TaskbarButton}" ToolTipService.ShowOnDisabled="True" Width="60">
<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
<Viewbox x:Name="SignalStrengthIcon" Stretch="Uniform" Width="Auto" />
<fa:ImageAwesome x:Name="NoAdapterIcon" Foreground="Red" Icon="Ban" Margin="1" Opacity="0.3" Visibility="Collapsed" />
<fa:ImageAwesome x:Name="LoadingIcon" Foreground="Gray" Icon="Cog" Margin="5" Spin="True" SpinDuration="2" Visibility="Collapsed" />
<Image x:Name="NetworkStatusIcon" Height="12" HorizontalAlignment="Right" Margin="0,2" Panel.ZIndex="10" VerticalAlignment="Bottom" />
<Viewbox x:Name="WirelessIcon" Stretch="Uniform" Width="Auto" Visibility="Collapsed" />
<fa:ImageAwesome x:Name="WiredIcon" Icon="Tv" Margin="0,2,4,4" Visibility="Collapsed" />
<Border Background="{StaticResource BackgroundBrush}" CornerRadius="6" Height="18" HorizontalAlignment="Right" Margin="0,0,-1,1" Panel.ZIndex="10" VerticalAlignment="Bottom">
<fa:ImageAwesome x:Name="NetworkStatusIcon" />
</Border>
</Grid>
</Button>
</Grid>

View file

@ -15,20 +15,20 @@ using System.Windows.Media;
using FontAwesome.WPF;
using SafeExamBrowser.Core.Contracts.Resources.Icons;
using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.SystemComponents.Contracts.WirelessNetwork;
using SafeExamBrowser.SystemComponents.Contracts.Network;
using SafeExamBrowser.UserInterface.Contracts.Shell;
using SafeExamBrowser.UserInterface.Shared.Utilities;
namespace SafeExamBrowser.UserInterface.Mobile.Controls.Taskbar
{
internal partial class WirelessNetworkControl : UserControl, ISystemControl
internal partial class NetworkControl : UserControl, ISystemControl
{
private IWirelessAdapter wirelessAdapter;
private IText text;
private readonly INetworkAdapter adapter;
private readonly IText text;
internal WirelessNetworkControl(IWirelessAdapter wirelessAdapter, IText text)
internal NetworkControl(INetworkAdapter adapter, IText text)
{
this.wirelessAdapter = wirelessAdapter;
this.adapter = adapter;
this.text = text;
InitializeComponent();
@ -44,11 +44,12 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls.Taskbar
{
var originalBrush = Button.Background;
SignalStrengthIcon.Child = GetIcon(0);
adapter.Changed += () => Dispatcher.InvokeAsync(Update);
Button.Click += (o, args) => Popup.IsOpen = !Popup.IsOpen;
Button.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => Popup.IsOpen = Popup.IsMouseOver));
Popup.CustomPopupPlacementCallback = new CustomPopupPlacementCallback(Popup_PlacementCallback);
Popup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => Popup.IsOpen = IsMouseOver));
WirelessIcon.Child = GetWirelessIcon(0);
Popup.Opened += (o, args) =>
{
@ -62,18 +63,7 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls.Taskbar
Button.Background = originalBrush;
};
if (wirelessAdapter.IsAvailable)
{
wirelessAdapter.NetworksChanged += WirelessAdapter_NetworksChanged;
wirelessAdapter.StatusChanged += WirelessAdapter_StatusChanged;
UpdateNetworks();
}
else
{
Button.IsEnabled = false;
NoAdapterIcon.Visibility = Visibility.Visible;
UpdateText(text.Get(TextKey.SystemControl_WirelessNotAvailable));
}
Update();
}
private CustomPopupPlacement[] Popup_PlacementCallback(Size popupSize, Size targetSize, Point offset)
@ -84,72 +74,70 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls.Taskbar
};
}
private void WirelessAdapter_NetworksChanged()
private void Update()
{
Dispatcher.InvokeAsync(UpdateNetworks);
}
WirelessNetworksStackPanel.Children.Clear();
private void WirelessAdapter_StatusChanged(WirelessNetworkStatus status)
{
Dispatcher.InvokeAsync(() => UpdateStatus(status));
}
private void UpdateNetworks()
{
var status = WirelessNetworkStatus.Disconnected;
NetworksStackPanel.Children.Clear();
foreach (var network in wirelessAdapter.GetNetworks())
foreach (var network in adapter.GetWirelessNetworks())
{
var button = new WirelessNetworkButton(network);
var button = new NetworkButton(network);
button.NetworkSelected += (o, args) => wirelessAdapter.Connect(network.Id);
button.NetworkSelected += (o, args) => adapter.ConnectToWirelessNetwork(network.Id);
if (network.Status == WirelessNetworkStatus.Connected)
if (network.Status == ConnectionStatus.Connected)
{
status = WirelessNetworkStatus.Connected;
SignalStrengthIcon.Child = GetIcon(network.SignalStrength);
UpdateText(text.Get(TextKey.SystemControl_WirelessConnected).Replace("%%NAME%%", network.Name));
WirelessIcon.Child = GetWirelessIcon(network.SignalStrength);
Button.ToolTip = text.Get(TextKey.SystemControl_NetworkWirelessConnected).Replace("%%NAME%%", network.Name);
}
NetworksStackPanel.Children.Add(button);
WirelessNetworksStackPanel.Children.Add(button);
}
UpdateStatus(status);
}
private void UpdateStatus(WirelessNetworkStatus status)
{
LoadingIcon.Visibility = Visibility.Collapsed;
SignalStrengthIcon.Visibility = Visibility.Visible;
NetworkStatusIcon.Visibility = Visibility.Visible;
switch (status)
switch (adapter.Type)
{
case WirelessNetworkStatus.Connected:
NetworkStatusIcon.Source = ImageAwesome.CreateImageSource(FontAwesomeIcon.Check, Brushes.Green);
case ConnectionType.Wired:
Button.IsEnabled = false;
Button.ToolTip = text.Get(TextKey.SystemControl_NetworkWiredConnected);
WiredIcon.Visibility = Visibility.Visible;
WirelessIcon.Visibility = Visibility.Collapsed;
break;
case WirelessNetworkStatus.Connecting:
LoadingIcon.Visibility = Visibility.Visible;
SignalStrengthIcon.Visibility = Visibility.Collapsed;
NetworkStatusIcon.Visibility = Visibility.Collapsed;
UpdateText(text.Get(TextKey.SystemControl_WirelessConnecting));
case ConnectionType.Wireless:
Button.IsEnabled = true;
WiredIcon.Visibility = Visibility.Collapsed;
WirelessIcon.Visibility = Visibility.Visible;
break;
default:
NetworkStatusIcon.Source = ImageAwesome.CreateImageSource(FontAwesomeIcon.Close, Brushes.Orange);
SignalStrengthIcon.Child = GetIcon(0);
UpdateText(text.Get(TextKey.SystemControl_WirelessDisconnected));
Button.IsEnabled = false;
Button.ToolTip = text.Get(TextKey.SystemControl_NetworkNotAvailable);
WiredIcon.Visibility = Visibility.Visible;
WirelessIcon.Visibility = Visibility.Collapsed;
break;
}
switch (adapter.Status)
{
case ConnectionStatus.Connected:
NetworkStatusIcon.Rotation = 0;
NetworkStatusIcon.Source = ImageAwesome.CreateImageSource(FontAwesomeIcon.Globe, Brushes.Green);
NetworkStatusIcon.Spin = false;
break;
case ConnectionStatus.Connecting:
Button.ToolTip = text.Get(TextKey.SystemControl_NetworkWirelessConnecting);
NetworkStatusIcon.Rotation = 0;
NetworkStatusIcon.Source = ImageAwesome.CreateImageSource(FontAwesomeIcon.Cog, Brushes.DimGray);
NetworkStatusIcon.Spin = true;
NetworkStatusIcon.SpinDuration = 2;
break;
default:
Button.ToolTip = text.Get(TextKey.SystemControl_NetworkDisconnected);
NetworkStatusIcon.Source = ImageAwesome.CreateImageSource(FontAwesomeIcon.Ban, Brushes.DarkOrange);
NetworkStatusIcon.Spin = false;
WirelessIcon.Child = GetWirelessIcon(0);
break;
}
}
private void UpdateText(string text)
{
Button.ToolTip = text;
}
private UIElement GetIcon(int signalStrength)
private UIElement GetWirelessIcon(int signalStrength)
{
var icon = signalStrength > 66 ? "100" : (signalStrength > 33 ? "66" : (signalStrength > 0 ? "33" : "0"));
var uri = new Uri($"pack://application:,,,/SafeExamBrowser.UserInterface.Mobile;component/Images/WiFi_{icon}.xaml");

View file

@ -110,11 +110,11 @@
<Compile Include="Controls\ActionCenter\QuitButton.xaml.cs">
<DependentUpon>QuitButton.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\ActionCenter\WirelessNetworkButton.xaml.cs">
<DependentUpon>WirelessNetworkButton.xaml</DependentUpon>
<Compile Include="Controls\ActionCenter\NetworkButton.xaml.cs">
<DependentUpon>NetworkButton.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\ActionCenter\WirelessNetworkControl.xaml.cs">
<DependentUpon>WirelessNetworkControl.xaml</DependentUpon>
<Compile Include="Controls\ActionCenter\NetworkControl.xaml.cs">
<DependentUpon>NetworkControl.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\Browser\DownloadItemControl.xaml.cs">
<DependentUpon>DownloadItemControl.xaml</DependentUpon>
@ -146,11 +146,11 @@
<Compile Include="Controls\Taskbar\QuitButton.xaml.cs">
<DependentUpon>QuitButton.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\Taskbar\WirelessNetworkButton.xaml.cs">
<DependentUpon>WirelessNetworkButton.xaml</DependentUpon>
<Compile Include="Controls\Taskbar\NetworkButton.xaml.cs">
<DependentUpon>NetworkButton.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\Taskbar\WirelessNetworkControl.xaml.cs">
<DependentUpon>WirelessNetworkControl.xaml</DependentUpon>
<Compile Include="Controls\Taskbar\NetworkControl.xaml.cs">
<DependentUpon>NetworkControl.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\Taskview\WindowControl.xaml.cs">
<DependentUpon>WindowControl.xaml</DependentUpon>
@ -321,11 +321,11 @@
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Controls\ActionCenter\WirelessNetworkButton.xaml">
<Page Include="Controls\ActionCenter\NetworkButton.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Controls\ActionCenter\WirelessNetworkControl.xaml">
<Page Include="Controls\ActionCenter\NetworkControl.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
@ -369,11 +369,11 @@
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Controls\Taskbar\WirelessNetworkButton.xaml">
<Page Include="Controls\Taskbar\NetworkButton.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Controls\Taskbar\WirelessNetworkControl.xaml">
<Page Include="Controls\Taskbar\NetworkControl.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>

View file

@ -22,8 +22,8 @@ using SafeExamBrowser.Settings.Browser;
using SafeExamBrowser.Settings.Proctoring;
using SafeExamBrowser.SystemComponents.Contracts.Audio;
using SafeExamBrowser.SystemComponents.Contracts.Keyboard;
using SafeExamBrowser.SystemComponents.Contracts.Network;
using SafeExamBrowser.SystemComponents.Contracts.PowerSupply;
using SafeExamBrowser.SystemComponents.Contracts.WirelessNetwork;
using SafeExamBrowser.UserInterface.Contracts;
using SafeExamBrowser.UserInterface.Contracts.Browser;
using SafeExamBrowser.UserInterface.Contracts.Proctoring;
@ -37,7 +37,7 @@ namespace SafeExamBrowser.UserInterface.Mobile
{
public class UserInterfaceFactory : IUserInterfaceFactory
{
private IText text;
private readonly IText text;
public UserInterfaceFactory(IText text)
{
@ -131,6 +131,18 @@ namespace SafeExamBrowser.UserInterface.Mobile
return window;
}
public ISystemControl CreateNetworkControl(INetworkAdapter adapter, Location location)
{
if (location == Location.ActionCenter)
{
return new Controls.ActionCenter.NetworkControl(adapter, text);
}
else
{
return new Controls.Taskbar.NetworkControl(adapter, text);
}
}
public INotificationControl CreateNotificationControl(INotification notification, Location location)
{
if (location == Location.ActionCenter)
@ -226,18 +238,6 @@ namespace SafeExamBrowser.UserInterface.Mobile
return new Taskview();
}
public ISystemControl CreateWirelessNetworkControl(IWirelessAdapter wirelessAdapter, Location location)
{
if (location == Location.ActionCenter)
{
return new Controls.ActionCenter.WirelessNetworkControl(wirelessAdapter, text);
}
else
{
return new Controls.Taskbar.WirelessNetworkControl(wirelessAdapter, text);
}
}
private void InitializeFontAwesome()
{
// To be able to use FontAwesome in XAML icon resources, we need to make sure that the FontAwesome.WPF assembly is loaded into

View file

@ -108,6 +108,11 @@ namespace SafeExamBrowser.WindowsApi.Contracts
/// </exception>
IBounds GetWorkingArea();
/// <summary>
/// Determines whether this computer is connected to the internet. Returns <c>true</c> if successful, otherwise <c>false</c>.
/// </summary>
bool HasInternetConnection();
/// <summary>
/// Hides the given window. Returns <c>true</c> if successful, otherwise <c>false</c>.
/// </summary>

View file

@ -24,9 +24,9 @@ namespace SafeExamBrowser.WindowsApi
{
public class NativeMethods : INativeMethods
{
private ConcurrentDictionary<Guid, KeyboardHook> KeyboardHooks = new ConcurrentDictionary<Guid, KeyboardHook>();
private ConcurrentDictionary<Guid, MouseHook> MouseHooks = new ConcurrentDictionary<Guid, MouseHook>();
private ConcurrentDictionary<Guid, SystemHook> SystemHooks = new ConcurrentDictionary<Guid, SystemHook>();
private readonly ConcurrentDictionary<Guid, KeyboardHook> KeyboardHooks = new ConcurrentDictionary<Guid, KeyboardHook>();
private readonly ConcurrentDictionary<Guid, MouseHook> MouseHooks = new ConcurrentDictionary<Guid, MouseHook>();
private readonly ConcurrentDictionary<Guid, SystemHook> SystemHooks = new ConcurrentDictionary<Guid, SystemHook>();
/// <summary>
/// Upon finalization, unregister all active system events and hooks...
@ -245,6 +245,11 @@ namespace SafeExamBrowser.WindowsApi
return workingArea.ToBounds();
}
public bool HasInternetConnection()
{
return WinInet.InternetGetConnectedState(out _, 0);
}
public bool HideWindow(IntPtr window)
{
return User32.ShowWindow(window, (int) ShowWindowCommand.Hide);

View file

@ -92,6 +92,7 @@
<Compile Include="Types\Window.cs" />
<Compile Include="Types\WINDOWPLACEMENT.cs" />
<Compile Include="User32.cs" />
<Compile Include="WinInet.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\SafeExamBrowser.Logging.Contracts\SafeExamBrowser.Logging.Contracts.csproj">

View file

@ -0,0 +1,21 @@
/*
* Copyright (c) 2022 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.Runtime.InteropServices;
namespace SafeExamBrowser.WindowsApi
{
/// <summary>
/// Provides access to the native Windows API exposed by <c>wininet.dll</c>.
/// </summary>
internal static class WinInet
{
[DllImport("wininet.dll", SetLastError = true)]
internal static extern bool InternetGetConnectedState(out int description, int reservedValue);
}
}