diff --git a/Libraries/SimpleWifi.dll b/Libraries/SimpleWifi.dll
deleted file mode 100644
index 3e28917a..00000000
Binary files a/Libraries/SimpleWifi.dll and /dev/null differ
diff --git a/SafeExamBrowser.Client/ClientController.cs b/SafeExamBrowser.Client/ClientController.cs
index 9c53f207..265e936e 100644
--- a/SafeExamBrowser.Client/ClientController.cs
+++ b/SafeExamBrowser.Client/ClientController.cs
@@ -698,7 +698,9 @@ namespace SafeExamBrowser.Client
private void NetworkAdapter_CredentialsRequired(CredentialsRequiredEventArgs args)
{
- var dialog = uiFactory.CreateNetworkDialog("TODO", "TODO");
+ var message = text.Get(TextKey.CredentialsDialog_WirelessNetworkMessage).Replace("%%_NAME_%%", args.NetworkName);
+ var title = text.Get(TextKey.CredentialsDialog_WirelessNetworkTitle);
+ var dialog = uiFactory.CreateCredentialsDialog(CredentialsDialogPurpose.WirelessNetwork, message, title);
var result = dialog.Show();
args.Password = result.Password;
diff --git a/SafeExamBrowser.Configuration/ConfigurationData/DataProcessor.cs b/SafeExamBrowser.Configuration/ConfigurationData/DataProcessor.cs
index d49961fe..61b84d98 100644
--- a/SafeExamBrowser.Configuration/ConfigurationData/DataProcessor.cs
+++ b/SafeExamBrowser.Configuration/ConfigurationData/DataProcessor.cs
@@ -26,26 +26,6 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
InitializeClipboardSettings(settings);
InitializeProctoringSettings(settings);
RemoveLegacyBrowsers(settings);
-
- settings.Applications.Blacklist.Clear();
- settings.Applications.Whitelist.Add(new WhitelistApplication { ExecutableName = "mspaint.exe", ShowInShell = true });
- settings.Browser.AdditionalWindow.AllowAddressBar = true;
- settings.Browser.MainWindow.AllowAddressBar = true;
- settings.Browser.MainWindow.AllowDeveloperConsole = true;
- settings.Browser.MainWindow.AllowBackwardNavigation = true;
- settings.Browser.MainWindow.AllowForwardNavigation = true;
- settings.Browser.MainWindow.ShowHomeButton = true;
- settings.Browser.MainWindow.UrlPolicy = Settings.Browser.UrlPolicy.BeforeTitle;
- settings.Keyboard.AllowPrintScreen = true;
- settings.LogLevel = Settings.Logging.LogLevel.Debug;
- settings.Security.AllowApplicationLogAccess = true;
- settings.Security.ClipboardPolicy = ClipboardPolicy.Allow;
- settings.Security.KioskMode = KioskMode.CreateNewDesktop;
- settings.Security.QuitPasswordHash = default;
- settings.Service.IgnoreService = true;
- settings.Service.Policy = Settings.Service.ServicePolicy.Optional;
- settings.Security.VersionRestrictions.Clear();
- settings.Taskbar.ShowApplicationLog = true;
}
private void AllowBrowserToolbarForReloading(AppSettings settings)
diff --git a/SafeExamBrowser.I18n.Contracts/TextKey.cs b/SafeExamBrowser.I18n.Contracts/TextKey.cs
index 6c17a2ea..72532e7c 100644
--- a/SafeExamBrowser.I18n.Contracts/TextKey.cs
+++ b/SafeExamBrowser.I18n.Contracts/TextKey.cs
@@ -47,6 +47,11 @@ namespace SafeExamBrowser.I18n.Contracts
BrowserWindow_ZoomMenuMinus,
BrowserWindow_ZoomMenuPlus,
Build,
+ CredentialsDialog_PasswordLabel,
+ CredentialsDialog_UsernameLabel,
+ CredentialsDialog_UsernameOptionalLabel,
+ CredentialsDialog_WirelessNetworkMessage,
+ CredentialsDialog_WirelessNetworkTitle,
ExamSelectionDialog_Cancel,
ExamSelectionDialog_Message,
ExamSelectionDialog_Select,
diff --git a/SafeExamBrowser.I18n/Data/de.xml b/SafeExamBrowser.I18n/Data/de.xml
index 0fe5b60b..004c68fa 100644
--- a/SafeExamBrowser.I18n/Data/de.xml
+++ b/SafeExamBrowser.I18n/Data/de.xml
@@ -99,6 +99,21 @@
Build
+
+ Passwort:
+
+
+ Benutzername:
+
+
+ Benutzername (optional):
+
+
+ Bitte geben Sie die erforderlichen Anmeldeinformationen ein, um eine Verbindung mit dem drahtlosen Netzwerk "%%_NAME_%%" herzustellen.
+
+
+ Netzwerk-Authentifizierung erforderlich
+
Abbrechen
diff --git a/SafeExamBrowser.I18n/Data/en.xml b/SafeExamBrowser.I18n/Data/en.xml
index 03e66143..9700fabc 100644
--- a/SafeExamBrowser.I18n/Data/en.xml
+++ b/SafeExamBrowser.I18n/Data/en.xml
@@ -99,6 +99,21 @@
Build
+
+ Password:
+
+
+ Username:
+
+
+ Username (optional):
+
+
+ Please enter the required credentials to establish a connection to the wireless network "%%_NAME_%%".
+
+
+ Network Authentication Required
+
Cancel
diff --git a/SafeExamBrowser.I18n/Data/es.xml b/SafeExamBrowser.I18n/Data/es.xml
index 5bd5d882..f4f24a5a 100644
--- a/SafeExamBrowser.I18n/Data/es.xml
+++ b/SafeExamBrowser.I18n/Data/es.xml
@@ -99,6 +99,21 @@
Build
+
+ Contraseña:
+
+
+ Nombre de usuario:
+
+
+ Nombre de usuario (opcional):
+
+
+ Por favor, introduzca las credenciales necesarias para establecer una conexión con la red inalámbrica "%%_NAME_%%".
+
+
+ Autenticación de red necesaria
+
Cancel
diff --git a/SafeExamBrowser.I18n/Data/et.xml b/SafeExamBrowser.I18n/Data/et.xml
index 2766190b..35ef2f74 100644
--- a/SafeExamBrowser.I18n/Data/et.xml
+++ b/SafeExamBrowser.I18n/Data/et.xml
@@ -99,6 +99,21 @@
Koosta
+
+ Parool:
+
+
+ Kasutajanimi:
+
+
+ Kasutajanimi (vabatahtlik):
+
+
+ Palun sisestage vajalikud volitused, et luua ühendus traadita võrguga "%%_NAME_%%".
+
+
+ Võrguautentimine nõutav
+
Tühista
diff --git a/SafeExamBrowser.I18n/Data/fr.xml b/SafeExamBrowser.I18n/Data/fr.xml
index 211a1127..a4ce8e44 100644
--- a/SafeExamBrowser.I18n/Data/fr.xml
+++ b/SafeExamBrowser.I18n/Data/fr.xml
@@ -99,6 +99,21 @@
Build
+
+ Mot de passe:
+
+
+ Nom d'utilisateur:
+
+
+ Nom d'utilisateur (facultatif):
+
+
+ Veuillez saisir les informations d'identification requises pour établir une connexion au réseau sans fil "%%_NAME_%%".
+
+
+ Authentification réseau requise
+
Annuler
diff --git a/SafeExamBrowser.I18n/Data/id.xml b/SafeExamBrowser.I18n/Data/id.xml
index f3fb69bc..f921fbec 100644
--- a/SafeExamBrowser.I18n/Data/id.xml
+++ b/SafeExamBrowser.I18n/Data/id.xml
@@ -99,6 +99,21 @@
Bangun
+
+ Kata sandi:
+
+
+ Nama pengguna:
+
+
+ Nama pengguna (opsional):
+
+
+ Masukkan kredensial yang diperlukan untuk membuat sambungan ke jaringan nirkabel "%%_NAMA_%%".
+
+
+ Diperlukan Otentikasi Jaringan
+
Batal
diff --git a/SafeExamBrowser.I18n/Data/it.xml b/SafeExamBrowser.I18n/Data/it.xml
index 35c157e1..ec33bbde 100644
--- a/SafeExamBrowser.I18n/Data/it.xml
+++ b/SafeExamBrowser.I18n/Data/it.xml
@@ -99,6 +99,21 @@
Build
+
+ Password:
+
+
+ Nome utente:
+
+
+ Nome utente (facoltativo):
+
+
+ Inserire le credenziali richieste per stabilire una connessione alla rete wireless "%%_NAME_%%".
+
+
+ È richiesta l'autenticazione di rete
+
Annulla
diff --git a/SafeExamBrowser.I18n/Data/nl.xml b/SafeExamBrowser.I18n/Data/nl.xml
index 93360459..54def0c2 100644
--- a/SafeExamBrowser.I18n/Data/nl.xml
+++ b/SafeExamBrowser.I18n/Data/nl.xml
@@ -99,6 +99,21 @@
Build
+
+ Wachtwoord:
+
+
+ Gebruikersnaam:
+
+
+ Gebruikersnaam (optioneel):
+
+
+ Voer de vereiste gegevens in om verbinding te maken met het draadloze netwerk “%%_NAME_%%”.
+
+
+ Netwerkverificatie vereist
+
Annuleren
diff --git a/SafeExamBrowser.I18n/Data/ru.xml b/SafeExamBrowser.I18n/Data/ru.xml
index 6ca6eb25..8651db4a 100644
--- a/SafeExamBrowser.I18n/Data/ru.xml
+++ b/SafeExamBrowser.I18n/Data/ru.xml
@@ -99,6 +99,21 @@
Строить
+
+ Пароль:
+
+
+ Имя пользователя:
+
+
+ Имя пользователя (необязательно):
+
+
+ Введите необходимые учетные данные для установления соединения с беспроводной сетью "%%_NAME_%%".
+
+
+ Требуется сетевая аутентификация
+
Отмена
diff --git a/SafeExamBrowser.I18n/Data/zh.xml b/SafeExamBrowser.I18n/Data/zh.xml
index 387ffaf2..2456befd 100644
--- a/SafeExamBrowser.I18n/Data/zh.xml
+++ b/SafeExamBrowser.I18n/Data/zh.xml
@@ -99,6 +99,21 @@
生成
+
+ 密码:
+
+
+ 用户名:
+
+
+ 用户名(可选):
+
+
+ 请输入与无线网络 "%%_NAME_%%" 建立连接所需的凭证。
+
+
+ 需要网络验证
+
取消
diff --git a/SafeExamBrowser.SystemComponents.Contracts/Network/Events/CredentialsRequiredEventArgs.cs b/SafeExamBrowser.SystemComponents.Contracts/Network/Events/CredentialsRequiredEventArgs.cs
index 111aff92..11a4ee63 100644
--- a/SafeExamBrowser.SystemComponents.Contracts/Network/Events/CredentialsRequiredEventArgs.cs
+++ b/SafeExamBrowser.SystemComponents.Contracts/Network/Events/CredentialsRequiredEventArgs.cs
@@ -9,22 +9,27 @@
namespace SafeExamBrowser.SystemComponents.Contracts.Network.Events
{
///
- ///
+ /// The event arguments for the .
///
public class CredentialsRequiredEventArgs
{
///
- ///
+ /// The name of the network which requires credentials.
+ ///
+ public string NetworkName { get; set; }
+
+ ///
+ /// The password as specified by the user.
///
public string Password { get; set; }
///
- ///
+ /// Indicates whether the credentials could be successfully retrieved or not.
///
public bool Success { get; set; }
///
- ///
+ /// The username as specified by the user.
///
public string Username { get; set; }
}
diff --git a/SafeExamBrowser.SystemComponents.Contracts/Network/Events/CredentialsRequiredEventHandler.cs b/SafeExamBrowser.SystemComponents.Contracts/Network/Events/CredentialsRequiredEventHandler.cs
index 888d97a6..08357bed 100644
--- a/SafeExamBrowser.SystemComponents.Contracts/Network/Events/CredentialsRequiredEventHandler.cs
+++ b/SafeExamBrowser.SystemComponents.Contracts/Network/Events/CredentialsRequiredEventHandler.cs
@@ -9,7 +9,7 @@
namespace SafeExamBrowser.SystemComponents.Contracts.Network.Events
{
///
- ///
+ /// Indicates that credentials are required to connect to a network.
///
public delegate void CredentialsRequiredEventHandler(CredentialsRequiredEventArgs args);
}
diff --git a/SafeExamBrowser.SystemComponents.Contracts/Network/INetworkAdapter.cs b/SafeExamBrowser.SystemComponents.Contracts/Network/INetworkAdapter.cs
index 207397cf..ef98a539 100644
--- a/SafeExamBrowser.SystemComponents.Contracts/Network/INetworkAdapter.cs
+++ b/SafeExamBrowser.SystemComponents.Contracts/Network/INetworkAdapter.cs
@@ -45,5 +45,15 @@ namespace SafeExamBrowser.SystemComponents.Contracts.Network
/// Retrieves all currently available wireless networks.
///
IEnumerable GetWirelessNetworks();
+
+ ///
+ /// Starts periodically scanning the available wireless networks.
+ ///
+ void StartWirelessNetworkScanning();
+
+ ///
+ /// Stops the periodical scanning of wireless networks.
+ ///
+ void StopWirelessNetworkScanning();
}
}
diff --git a/SafeExamBrowser.SystemComponents/Network/Extensions.cs b/SafeExamBrowser.SystemComponents/Network/Extensions.cs
new file mode 100644
index 00000000..01590732
--- /dev/null
+++ b/SafeExamBrowser.SystemComponents/Network/Extensions.cs
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2024 ETH Zürich, IT Services
+ *
+ * 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 SafeExamBrowser.SystemComponents.Contracts.Network;
+using Windows.Devices.WiFi;
+
+namespace SafeExamBrowser.SystemComponents.Network
+{
+ internal static class Extensions
+ {
+ internal static IOrderedEnumerable FilterAndOrder(this IReadOnlyList networks)
+ {
+ return networks.Where(n => !string.IsNullOrEmpty(n.Ssid)).GroupBy(n => n.Ssid).Select(g => g.First()).OrderBy(n => n.Ssid);
+ }
+
+ internal static WirelessNetwork ToWirelessNetwork(this WiFiAvailableNetwork network)
+ {
+ return new WirelessNetwork
+ {
+ Name = network.Ssid,
+ Network = network,
+ SignalStrength = Convert.ToInt32(Math.Max(0, Math.Min(100, (network.NetworkRssiInDecibelMilliwatts + 100) * 2))),
+ Status = ConnectionStatus.Disconnected
+ };
+ }
+ }
+}
diff --git a/SafeExamBrowser.SystemComponents/Network/NetworkAdapter.cs b/SafeExamBrowser.SystemComponents/Network/NetworkAdapter.cs
index 124cfadc..144b364a 100644
--- a/SafeExamBrowser.SystemComponents/Network/NetworkAdapter.cs
+++ b/SafeExamBrowser.SystemComponents/Network/NetworkAdapter.cs
@@ -14,21 +14,15 @@ 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;
+using Windows.Devices.Enumeration;
+using Windows.Devices.WiFi;
+using Windows.Foundation;
+using Windows.Networking.Connectivity;
+using Windows.Security.Credentials;
using Timer = System.Timers.Timer;
namespace SafeExamBrowser.SystemComponents.Network
{
- ///
- /// Switch to the following WiFi library:
- /// https://github.com/emoacht/ManagedNativeWifi
- /// https://www.nuget.org/packages/ManagedNativeWifi
- ///
- /// Potentially useful:
- /// https://learn.microsoft.com/en-us/dotnet/api/system.net.networkinformation.networkinterface?view=netframework-4.8
- ///
public class NetworkAdapter : INetworkAdapter
{
private readonly object @lock = new object();
@@ -38,7 +32,9 @@ namespace SafeExamBrowser.SystemComponents.Network
private readonly List wirelessNetworks;
private Timer timer;
- private Wifi wifi;
+ private WiFiAdapter adapter;
+
+ private bool HasWirelessAdapter => adapter != default;
public ConnectionStatus Status { get; private set; }
public ConnectionType Type { get; private set; }
@@ -55,36 +51,36 @@ namespace SafeExamBrowser.SystemComponents.Network
public void ConnectToWirelessNetwork(string name)
{
+ var network = default(WiFiAvailableNetwork);
+
lock (@lock)
{
- var network = wirelessNetworks.FirstOrDefault(n => n.Name == name);
+ network = wirelessNetworks.FirstOrDefault(n => n.Name == name)?.Network;
+ }
- if (network != default)
+ if (network != default)
+ {
+ var isOpen = network.SecuritySettings.NetworkAuthenticationType == NetworkAuthenticationType.Open80211 && network.SecuritySettings.NetworkEncryptionType == NetworkEncryptionType.None;
+
+ if (isOpen)
{
- try
- {
- var accessPoint = network.AccessPoint;
- var request = new AuthRequest(accessPoint);
+ logger.Info($"Attempting to connect to open wireless network '{name}'...");
- if (accessPoint.HasProfile || accessPoint.IsConnected || TryGetCredentials(request))
- {
- logger.Info($"Attempting to connect to wireless network '{network.Name}' with{(request.Password == default ? "out" : "")} credentials...");
-
- // TODO: Retry resp. alert of password error on failure and then ignore profile?!
- accessPoint.ConnectAsync(request, false, (success) => ConnectionAttemptCompleted(network.Name, success));
- Status = ConnectionStatus.Connecting;
- }
- }
- catch (Exception e)
- {
- logger.Error($"Failed to connect to wireless network '{network.Name}!'", e);
- }
+ adapter.ConnectAsync(network, WiFiReconnectionKind.Automatic).Completed = (o, s) => Adapter_ConnectCompleted(name, o, s);
+ Status = ConnectionStatus.Connecting;
}
- else
+ else if (TryGetCredentials(name, out var credentials))
{
- logger.Warn($"Could not find wireless network '{name}'!");
+ logger.Info($"Attempting to connect to wireless network '{name}' with credentials...");
+
+ adapter.ConnectAsync(network, WiFiReconnectionKind.Automatic, credentials).Completed = (o, s) => Adapter_ConnectCompleted(name, o, s);
+ Status = ConnectionStatus.Connecting;
}
}
+ else
+ {
+ logger.Warn($"Could not find wireless network '{name}'!");
+ }
Changed?.Invoke();
}
@@ -101,103 +97,233 @@ namespace SafeExamBrowser.SystemComponents.Network
{
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();
+
+ InitializeAdapter();
+
+ NetworkChange.NetworkAddressChanged += NetworkChange_NetworkAddressChanged;
+ NetworkChange.NetworkAvailabilityChanged += NetworkChange_NetworkAvailabilityChanged;
+ NetworkInformation.NetworkStatusChanged += NetworkInformation_NetworkStatusChanged;
Update();
logger.Info("Started monitoring the network adapter.");
}
- public void Terminate()
+ public void StartWirelessNetworkScanning()
{
- if (timer != null)
+ timer?.Start();
+
+ if (HasWirelessAdapter)
{
- timer.Stop();
- logger.Info("Stopped monitoring the network adapter.");
+ _ = adapter.ScanAsync();
}
}
- private void ConnectionAttemptCompleted(string name, bool success)
+ public void StopWirelessNetworkScanning()
{
- lock (@lock)
+ timer?.Stop();
+ }
+
+ public void Terminate()
+ {
+ NetworkChange.NetworkAddressChanged -= NetworkChange_NetworkAddressChanged;
+ NetworkChange.NetworkAvailabilityChanged -= NetworkChange_NetworkAvailabilityChanged;
+ NetworkInformation.NetworkStatusChanged -= NetworkInformation_NetworkStatusChanged;
+
+ if (HasWirelessAdapter)
{
- // 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;
+ adapter.AvailableNetworksChanged -= Adapter_AvailableNetworksChanged;
}
- if (success)
+ if (timer != default)
{
+ timer.Stop();
+ }
+
+ logger.Info("Stopped monitoring the network adapter.");
+ }
+
+ private void InitializeAdapter()
+ {
+ try
+ {
+ // Requesting access is required as of fall 2024 and must be granted manually by the user, otherwise all wireless functionality will
+ // be denied by the system (see also https://learn.microsoft.com/en-us/windows/win32/nativewifi/wi-fi-access-location-changes).
+ var task = WiFiAdapter.RequestAccessAsync().AsTask();
+ var status = task.GetAwaiter().GetResult();
+
+ if (status == WiFiAccessStatus.Allowed)
+ {
+ var findAll = DeviceInformation.FindAllAsync(WiFiAdapter.GetDeviceSelector()).AsTask();
+ var devices = findAll.GetAwaiter().GetResult();
+
+ if (devices.Any())
+ {
+ var id = devices.First().Id;
+ var getById = WiFiAdapter.FromIdAsync(id).AsTask();
+
+ logger.Debug($"Found {devices.Count()} wireless network adapter(s).");
+
+ adapter = getById.GetAwaiter().GetResult();
+ adapter.AvailableNetworksChanged += Adapter_AvailableNetworksChanged;
+
+ logger.Debug($"Successfully initialized wireless network adapter '{id}'.");
+ }
+ else
+ {
+ logger.Info("Could not find a wireless network adapter.");
+ }
+ }
+ else
+ {
+ logger.Error($"Access to the wireless network adapter has been denied ({status})!");
+ }
+ }
+ catch (Exception e)
+ {
+ logger.Error("Failed to initialize wireless network adapter!", e);
+ }
+ }
+
+ private void Adapter_AvailableNetworksChanged(WiFiAdapter sender, object args)
+ {
+ Update(false);
+ }
+
+ private void Adapter_ConnectCompleted(string name, IAsyncOperation operation, AsyncStatus status)
+ {
+ var connectionStatus = default(WiFiConnectionStatus?);
+
+ if (status == AsyncStatus.Completed)
+ {
+ connectionStatus = operation.GetResults()?.ConnectionStatus;
+ }
+ else
+ {
+ logger.Error($"Failed to complete connection operation! Status: {status}.");
+ }
+
+ if (connectionStatus == WiFiConnectionStatus.Success)
+ {
+ Status = ConnectionStatus.Connected;
logger.Info($"Successfully connected to wireless network '{name}'.");
}
else
{
- logger.Error($"Failed to connect to wireless network '{name}!'");
+ Status = ConnectionStatus.Disconnected;
+ logger.Error($"Failed to connect to wireless network '{name}'! Reason: {connectionStatus}.");
}
+
+ Update();
}
- private bool TryGetCredentials(AuthRequest request)
+ private void NetworkChange_NetworkAddressChanged(object sender, EventArgs e)
{
- var args = new CredentialsRequiredEventArgs();
+ logger.Debug("Network address changed.");
+ Update();
+ }
+
+ private void NetworkChange_NetworkAvailabilityChanged(object sender, NetworkAvailabilityEventArgs e)
+ {
+ logger.Debug($"Network availability changed ({(e.IsAvailable ? "available" : "unavailable")}.");
+ Update();
+ }
+
+ private void NetworkInformation_NetworkStatusChanged(object sender)
+ {
+ logger.Debug("Network status changed.");
+ Update();
+ }
+
+ private bool TryGetCredentials(string network, out PasswordCredential credentials)
+ {
+ var args = new CredentialsRequiredEventArgs { NetworkName = network };
+
+ credentials = new PasswordCredential();
CredentialsRequired?.Invoke(args);
if (args.Success)
{
- request.Password = args.Password;
- request.Username = args.Username;
+ if (!string.IsNullOrEmpty(args.Password))
+ {
+ credentials.Password = args.Password;
+ }
+
+ if (!string.IsNullOrEmpty(args.Username))
+ {
+ credentials.UserName = args.Username;
+ }
}
return args.Success;
}
- private void Update()
+ private bool TryGetCurrentWirelessNetwork(out string name)
+ {
+ name = default;
+
+ if (HasWirelessAdapter)
+ {
+ try
+ {
+ var getProfile = adapter.NetworkAdapter.GetConnectedProfileAsync().AsTask();
+ var profile = getProfile.GetAwaiter().GetResult();
+
+ if (profile?.IsWlanConnectionProfile == true)
+ {
+ name = profile.WlanConnectionProfileDetails.GetConnectedSsid();
+ }
+ }
+ catch
+ {
+ }
+ }
+
+ return name != default;
+ }
+
+ private void Update(bool rescan = true)
{
try
{
lock (@lock)
{
- var current = default(WirelessNetwork);
var hasInternet = nativeMethods.HasInternetConnection();
- var hasWireless = !wifi.NoWifiAvailable && !IsTurnedOff();
var isConnecting = Status == ConnectionStatus.Connecting;
var previousStatus = Status;
wirelessNetworks.Clear();
- if (hasWireless)
+ if (HasWirelessAdapter)
{
- foreach (var wirelessNetwork in wifi.GetAccessPoints().Select(a => ToWirelessNetwork(a)))
- {
- wirelessNetworks.Add(wirelessNetwork);
+ TryGetCurrentWirelessNetwork(out var currentNetwork);
- if (wirelessNetwork.Status == ConnectionStatus.Connected)
+ foreach (var network in adapter.NetworkReport.AvailableNetworks.FilterAndOrder())
+ {
+ var wirelessNetwork = network.ToWirelessNetwork();
+
+ if (network.Ssid == currentNetwork)
{
- current = wirelessNetwork;
+ wirelessNetwork.Status = ConnectionStatus.Connected;
}
+
+ wirelessNetworks.Add(wirelessNetwork);
+ }
+
+ if (rescan)
+ {
+ _ = adapter.ScanAsync();
}
}
- Type = hasWireless ? ConnectionType.Wireless : (hasInternet ? ConnectionType.Wired : ConnectionType.Undefined);
- Status = hasInternet ? ConnectionStatus.Connected : (hasWireless && isConnecting ? ConnectionStatus.Connecting : ConnectionStatus.Disconnected);
+ Type = HasWirelessAdapter ? ConnectionType.Wireless : (hasInternet ? ConnectionType.Wired : ConnectionType.Undefined);
+ Status = hasInternet ? ConnectionStatus.Connected : (HasWirelessAdapter && isConnecting ? ConnectionStatus.Connecting : ConnectionStatus.Disconnected);
- if (previousStatus != ConnectionStatus.Connected && Status == ConnectionStatus.Connected)
- {
- logger.Info($"Connection established ({Type}{(current != default ? $", {current.Name}" : "")}).");
- }
-
- if (previousStatus != ConnectionStatus.Disconnected && Status == ConnectionStatus.Disconnected)
- {
- logger.Info("Connection lost.");
- }
+ LogNetworkChanges(previousStatus, wirelessNetworks.FirstOrDefault(n => n.Status == ConnectionStatus.Connected));
}
}
catch (Exception e)
@@ -208,40 +334,17 @@ namespace SafeExamBrowser.SystemComponents.Network
Changed?.Invoke();
}
- private bool IsTurnedOff()
+ private void LogNetworkChanges(ConnectionStatus previousStatus, WirelessNetwork currentNetwork)
{
- try
+ if (previousStatus != ConnectionStatus.Connected && Status == ConnectionStatus.Connected)
{
- 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);
+ logger.Info($"Connection established ({Type}{(currentNetwork != default ? $", {currentNetwork.Name}" : "")}).");
}
- return true;
- }
-
- private WirelessNetwork ToWirelessNetwork(AccessPoint accessPoint)
- {
- return new WirelessNetwork
+ if (previousStatus != ConnectionStatus.Disconnected && Status == ConnectionStatus.Disconnected)
{
- AccessPoint = accessPoint,
- Name = accessPoint.Name,
- SignalStrength = Convert.ToInt32(accessPoint.SignalStrength),
- Status = accessPoint.IsConnected ? ConnectionStatus.Connected : ConnectionStatus.Disconnected
- };
+ logger.Info("Connection lost.");
+ }
}
}
}
diff --git a/SafeExamBrowser.SystemComponents/Network/WirelessNetwork.cs b/SafeExamBrowser.SystemComponents/Network/WirelessNetwork.cs
index 35bce4cf..4dca409f 100644
--- a/SafeExamBrowser.SystemComponents/Network/WirelessNetwork.cs
+++ b/SafeExamBrowser.SystemComponents/Network/WirelessNetwork.cs
@@ -7,13 +7,13 @@
*/
using SafeExamBrowser.SystemComponents.Contracts.Network;
-using SimpleWifi;
+using Windows.Devices.WiFi;
namespace SafeExamBrowser.SystemComponents.Network
{
internal class WirelessNetwork : IWirelessNetwork
{
- internal AccessPoint AccessPoint { get; set; }
+ internal WiFiAvailableNetwork Network { get; set; }
public string Name { get; set; }
public int SignalStrength { get; set; }
diff --git a/SafeExamBrowser.SystemComponents/PowerSupply/PowerSupply.cs b/SafeExamBrowser.SystemComponents/PowerSupply/PowerSupply.cs
index 3d693af0..8a2805c4 100644
--- a/SafeExamBrowser.SystemComponents/PowerSupply/PowerSupply.cs
+++ b/SafeExamBrowser.SystemComponents/PowerSupply/PowerSupply.cs
@@ -58,12 +58,12 @@ namespace SafeExamBrowser.SystemComponents.PowerSupply
public void Initialize()
{
- const int TWO_SECONDS = 2000;
+ const int FIVE_SECONDS = 5000;
critical = SanitizeThreshold(settings.ChargeThresholdCritical);
low = SanitizeThreshold(settings.ChargeThresholdLow);
- timer = new Timer(TWO_SECONDS);
+ timer = new Timer(FIVE_SECONDS);
timer.Elapsed += Timer_Elapsed;
timer.AutoReset = true;
timer.Start();
diff --git a/SafeExamBrowser.SystemComponents/SafeExamBrowser.SystemComponents.csproj b/SafeExamBrowser.SystemComponents/SafeExamBrowser.SystemComponents.csproj
index ec0ae697..4e783d5e 100644
--- a/SafeExamBrowser.SystemComponents/SafeExamBrowser.SystemComponents.csproj
+++ b/SafeExamBrowser.SystemComponents/SafeExamBrowser.SystemComponents.csproj
@@ -49,44 +49,10 @@
MinimumRecommendedRules.ruleset
-
- ..\packages\Microsoft.Win32.Registry.5.0.0\lib\net461\Microsoft.Win32.Registry.dll
-
-
- ..\packages\NAudio.2.2.1\lib\net472\NAudio.dll
-
-
- ..\packages\NAudio.Asio.2.2.1\lib\netstandard2.0\NAudio.Asio.dll
-
-
- ..\packages\NAudio.Core.2.2.1\lib\netstandard2.0\NAudio.Core.dll
-
-
- ..\packages\NAudio.Midi.2.2.1\lib\netstandard2.0\NAudio.Midi.dll
-
-
- ..\packages\NAudio.Wasapi.2.2.1\lib\netstandard2.0\NAudio.Wasapi.dll
-
-
- ..\packages\NAudio.WinForms.2.2.1\lib\net472\NAudio.WinForms.dll
-
-
- ..\packages\NAudio.WinMM.2.2.1\lib\netstandard2.0\NAudio.WinMM.dll
-
-
- False
- ..\Libraries\SimpleWifi.dll
-
-
- ..\packages\System.Security.AccessControl.6.0.0\lib\net461\System.Security.AccessControl.dll
-
-
- ..\packages\System.Security.Principal.Windows.5.0.0\lib\net461\System.Security.Principal.Windows.dll
-
@@ -94,6 +60,7 @@
+
@@ -125,7 +92,20 @@
-
+
+
+
+ 5.0.0
+
+
+ 10.0.17134.1000
+
+
+ 2.2.1
+
+
+ 6.0.1
+
\ No newline at end of file
diff --git a/SafeExamBrowser.SystemComponents/packages.config b/SafeExamBrowser.SystemComponents/packages.config
deleted file mode 100644
index 22ee678a..00000000
--- a/SafeExamBrowser.SystemComponents/packages.config
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/SafeExamBrowser.UserInterface.Contracts/IUserInterfaceFactory.cs b/SafeExamBrowser.UserInterface.Contracts/IUserInterfaceFactory.cs
index 94825649..21bf2a80 100644
--- a/SafeExamBrowser.UserInterface.Contracts/IUserInterfaceFactory.cs
+++ b/SafeExamBrowser.UserInterface.Contracts/IUserInterfaceFactory.cs
@@ -58,6 +58,11 @@ namespace SafeExamBrowser.UserInterface.Contracts
///
IBrowserWindow CreateBrowserWindow(IBrowserControl control, BrowserSettings settings, bool isMainWindow, ILogger logger);
+ ///
+ /// Creates a credentials dialog for the given purpose and with the specified message and title.
+ ///
+ ICredentialsDialog CreateCredentialsDialog(CredentialsDialogPurpose purpose, string message, string title);
+
///
/// Creates an exam selection dialog for the given exams.
///
@@ -83,11 +88,6 @@ namespace SafeExamBrowser.UserInterface.Contracts
///
ISystemControl CreateNetworkControl(INetworkAdapter adapter, Location location);
- ///
- /// Creates a network dialog with the given message and title.
- ///
- INetworkDialog CreateNetworkDialog(string message, string title);
-
///
/// Creates a notification control for the given notification, initialized for the specified location.
///
diff --git a/SafeExamBrowser.UserInterface.Contracts/SafeExamBrowser.UserInterface.Contracts.csproj b/SafeExamBrowser.UserInterface.Contracts/SafeExamBrowser.UserInterface.Contracts.csproj
index 698d2010..cf090763 100644
--- a/SafeExamBrowser.UserInterface.Contracts/SafeExamBrowser.UserInterface.Contracts.csproj
+++ b/SafeExamBrowser.UserInterface.Contracts/SafeExamBrowser.UserInterface.Contracts.csproj
@@ -93,16 +93,17 @@
+
-
+
-
+
diff --git a/SafeExamBrowser.UserInterface.Contracts/Windows/Data/CredentialsDialogPurpose.cs b/SafeExamBrowser.UserInterface.Contracts/Windows/Data/CredentialsDialogPurpose.cs
new file mode 100644
index 00000000..5f7686ec
--- /dev/null
+++ b/SafeExamBrowser.UserInterface.Contracts/Windows/Data/CredentialsDialogPurpose.cs
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2024 ETH Zürich, IT Services
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+namespace SafeExamBrowser.UserInterface.Contracts.Windows.Data
+{
+ ///
+ /// Defines the purpose of a .
+ ///
+ public enum CredentialsDialogPurpose
+ {
+ ///
+ /// Credentials for generic purposes.
+ ///
+ Generic,
+
+ ///
+ /// Credentials for wireless network authentication.
+ ///
+ WirelessNetwork
+ }
+}
diff --git a/SafeExamBrowser.UserInterface.Contracts/Windows/Data/NetworkDialogResult.cs b/SafeExamBrowser.UserInterface.Contracts/Windows/Data/CredentialsDialogResult.cs
similarity index 88%
rename from SafeExamBrowser.UserInterface.Contracts/Windows/Data/NetworkDialogResult.cs
rename to SafeExamBrowser.UserInterface.Contracts/Windows/Data/CredentialsDialogResult.cs
index 439814d4..d2686039 100644
--- a/SafeExamBrowser.UserInterface.Contracts/Windows/Data/NetworkDialogResult.cs
+++ b/SafeExamBrowser.UserInterface.Contracts/Windows/Data/CredentialsDialogResult.cs
@@ -9,9 +9,9 @@
namespace SafeExamBrowser.UserInterface.Contracts.Windows.Data
{
///
- /// Defines the user interaction result of an .
+ /// Defines the user interaction result of an .
///
- public class NetworkDialogResult
+ public class CredentialsDialogResult
{
///
/// The password entered by the user, or default(string) if the interaction was unsuccessful.
diff --git a/SafeExamBrowser.UserInterface.Contracts/Windows/INetworkDialog.cs b/SafeExamBrowser.UserInterface.Contracts/Windows/ICredentialsDialog.cs
similarity index 76%
rename from SafeExamBrowser.UserInterface.Contracts/Windows/INetworkDialog.cs
rename to SafeExamBrowser.UserInterface.Contracts/Windows/ICredentialsDialog.cs
index a5c347e5..b2b3ff8b 100644
--- a/SafeExamBrowser.UserInterface.Contracts/Windows/INetworkDialog.cs
+++ b/SafeExamBrowser.UserInterface.Contracts/Windows/ICredentialsDialog.cs
@@ -11,13 +11,13 @@ using SafeExamBrowser.UserInterface.Contracts.Windows.Data;
namespace SafeExamBrowser.UserInterface.Contracts.Windows
{
///
- /// Defines the functionality of a network dialog.
+ /// Defines the functionality of a dialog to retrieve user credentials.
///
- public interface INetworkDialog : IWindow
+ public interface ICredentialsDialog : IWindow
{
///
/// Shows the dialog as topmost window. If a parent window is specified, the dialog is rendered modally for the given parent.
///
- NetworkDialogResult Show(IWindow parent = null);
+ CredentialsDialogResult Show(IWindow parent = null);
}
}
diff --git a/SafeExamBrowser.UserInterface.Desktop/Controls/ActionCenter/NetworkControl.xaml.cs b/SafeExamBrowser.UserInterface.Desktop/Controls/ActionCenter/NetworkControl.xaml.cs
index 72c03e1c..ee636b9c 100644
--- a/SafeExamBrowser.UserInterface.Desktop/Controls/ActionCenter/NetworkControl.xaml.cs
+++ b/SafeExamBrowser.UserInterface.Desktop/Controls/ActionCenter/NetworkControl.xaml.cs
@@ -63,6 +63,12 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls.ActionCenter
}
Popup.IsOpen = Popup.IsMouseOver;
}));
+ Popup.Closed += (o, args) =>
+ {
+ adapter.StopWirelessNetworkScanning();
+ Grid.Background = originalBrush;
+ lastOpenedBySpacePress = false;
+ };
Popup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() =>
{
if (Popup.IsOpen && lastOpenedBySpacePress)
@@ -73,6 +79,7 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls.ActionCenter
}));
Popup.Opened += (o, args) =>
{
+ adapter.StartWirelessNetworkScanning();
Grid.Background = Brushes.Gray;
Task.Delay(100).ContinueWith((task) => Dispatcher.Invoke(() =>
{
@@ -82,11 +89,6 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls.ActionCenter
}
}));
};
- Popup.Closed += (o, args) =>
- {
- Grid.Background = originalBrush;
- lastOpenedBySpacePress = false;
- };
WirelessIcon.Child = GetWirelessIcon(0);
Update();
@@ -94,23 +96,6 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls.ActionCenter
private void Update()
{
- WirelessNetworksStackPanel.Children.Clear();
-
- foreach (var network in adapter.GetWirelessNetworks())
- {
- var button = new NetworkButton(network);
-
- button.NetworkSelected += (o, args) => adapter.ConnectToWirelessNetwork(network.Name);
-
- 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:
@@ -154,6 +139,23 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls.ActionCenter
WirelessIcon.Child = GetWirelessIcon(0);
break;
}
+
+ WirelessNetworksStackPanel.Children.Clear();
+
+ foreach (var network in adapter.GetWirelessNetworks())
+ {
+ var button = new NetworkButton(network);
+
+ button.NetworkSelected += (o, args) => adapter.ConnectToWirelessNetwork(network.Name);
+
+ if (network.Status == ConnectionStatus.Connected)
+ {
+ WirelessIcon.Child = GetWirelessIcon(network.SignalStrength);
+ UpdateText(text.Get(TextKey.SystemControl_NetworkWirelessConnected).Replace("%%NAME%%", network.Name));
+ }
+
+ WirelessNetworksStackPanel.Children.Add(button);
+ }
}
private void UpdateText(string text)
diff --git a/SafeExamBrowser.UserInterface.Desktop/Controls/Taskbar/NetworkControl.xaml.cs b/SafeExamBrowser.UserInterface.Desktop/Controls/Taskbar/NetworkControl.xaml.cs
index 8f058b05..1d2cf1c2 100644
--- a/SafeExamBrowser.UserInterface.Desktop/Controls/Taskbar/NetworkControl.xaml.cs
+++ b/SafeExamBrowser.UserInterface.Desktop/Controls/Taskbar/NetworkControl.xaml.cs
@@ -66,6 +66,7 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls.Taskbar
}));
Popup.Closed += (o, args) =>
{
+ adapter.StopWirelessNetworkScanning();
Background = originalBrush;
Button.Background = originalBrush;
lastOpenedBySpacePress = false;
@@ -81,6 +82,7 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls.Taskbar
}));
Popup.Opened += (o, args) =>
{
+ adapter.StartWirelessNetworkScanning();
Background = Brushes.LightGray;
Button.Background = Brushes.LightGray;
Task.Delay(100).ContinueWith((task) => Dispatcher.Invoke(() =>
@@ -106,23 +108,6 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls.Taskbar
private void Update()
{
- WirelessNetworksStackPanel.Children.Clear();
-
- foreach (var network in adapter.GetWirelessNetworks())
- {
- var button = new NetworkButton(network);
-
- button.NetworkSelected += (o, args) => adapter.ConnectToWirelessNetwork(network.Name);
-
- 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:
@@ -166,6 +151,23 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls.Taskbar
WirelessIcon.Child = GetWirelessIcon(0);
break;
}
+
+ WirelessNetworksStackPanel.Children.Clear();
+
+ foreach (var network in adapter.GetWirelessNetworks())
+ {
+ var button = new NetworkButton(network);
+
+ button.NetworkSelected += (o, args) => adapter.ConnectToWirelessNetwork(network.Name);
+
+ if (network.Status == ConnectionStatus.Connected)
+ {
+ WirelessIcon.Child = GetWirelessIcon(network.SignalStrength);
+ UpdateText(text.Get(TextKey.SystemControl_NetworkWirelessConnected).Replace("%%NAME%%", network.Name));
+ }
+
+ WirelessNetworksStackPanel.Children.Add(button);
+ }
}
private void UpdateText(string text)
diff --git a/SafeExamBrowser.UserInterface.Desktop/SafeExamBrowser.UserInterface.Desktop.csproj b/SafeExamBrowser.UserInterface.Desktop/SafeExamBrowser.UserInterface.Desktop.csproj
index e9e76f25..d67030bc 100644
--- a/SafeExamBrowser.UserInterface.Desktop/SafeExamBrowser.UserInterface.Desktop.csproj
+++ b/SafeExamBrowser.UserInterface.Desktop/SafeExamBrowser.UserInterface.Desktop.csproj
@@ -168,8 +168,8 @@
LogWindow.xaml
-
- NetworkDialog.xaml
+
+ CredentialsDialog.xaml
PasswordDialog.xaml
@@ -397,7 +397,7 @@
Designer
MSBuild:Compile
-
+
Designer
MSBuild:Compile
diff --git a/SafeExamBrowser.UserInterface.Desktop/UserInterfaceFactory.cs b/SafeExamBrowser.UserInterface.Desktop/UserInterfaceFactory.cs
index 597e7840..a1019d80 100644
--- a/SafeExamBrowser.UserInterface.Desktop/UserInterfaceFactory.cs
+++ b/SafeExamBrowser.UserInterface.Desktop/UserInterfaceFactory.cs
@@ -85,6 +85,11 @@ namespace SafeExamBrowser.UserInterface.Desktop
return Application.Current.Dispatcher.Invoke(() => new BrowserWindow(control, settings, isMainWindow, text, logger));
}
+ public ICredentialsDialog CreateCredentialsDialog(CredentialsDialogPurpose purpose, string message, string title)
+ {
+ return Application.Current.Dispatcher.Invoke(() => new CredentialsDialog(purpose, message, title, text));
+ }
+
public IExamSelectionDialog CreateExamSelectionDialog(IEnumerable exams)
{
return Application.Current.Dispatcher.Invoke(() => new ExamSelectionDialog(exams, text));
@@ -143,11 +148,6 @@ namespace SafeExamBrowser.UserInterface.Desktop
}
}
- public INetworkDialog CreateNetworkDialog(string message, string title)
- {
- return Application.Current.Dispatcher.Invoke(() => new NetworkDialog(message, title, text));
- }
-
public INotificationControl CreateNotificationControl(INotification notification, Location location)
{
if (location == Location.ActionCenter)
diff --git a/SafeExamBrowser.UserInterface.Desktop/Windows/NetworkDialog.xaml b/SafeExamBrowser.UserInterface.Desktop/Windows/CredentialsDialog.xaml
similarity index 76%
rename from SafeExamBrowser.UserInterface.Desktop/Windows/NetworkDialog.xaml
rename to SafeExamBrowser.UserInterface.Desktop/Windows/CredentialsDialog.xaml
index 29a4c611..ee646bd0 100644
--- a/SafeExamBrowser.UserInterface.Desktop/Windows/NetworkDialog.xaml
+++ b/SafeExamBrowser.UserInterface.Desktop/Windows/CredentialsDialog.xaml
@@ -1,11 +1,11 @@
-
+ mc:Ignorable="d" Height="325" Width="450" ResizeMode="NoResize" Topmost="True">
@@ -15,17 +15,17 @@
-
-
+
+
-
+
-
-
+
+
@@ -33,18 +33,18 @@
-
+
-
+
-
+
diff --git a/SafeExamBrowser.UserInterface.Desktop/Windows/NetworkDialog.xaml.cs b/SafeExamBrowser.UserInterface.Desktop/Windows/CredentialsDialog.xaml.cs
similarity index 70%
rename from SafeExamBrowser.UserInterface.Desktop/Windows/NetworkDialog.xaml.cs
rename to SafeExamBrowser.UserInterface.Desktop/Windows/CredentialsDialog.xaml.cs
index 1188a276..74c4dfdb 100644
--- a/SafeExamBrowser.UserInterface.Desktop/Windows/NetworkDialog.xaml.cs
+++ b/SafeExamBrowser.UserInterface.Desktop/Windows/CredentialsDialog.xaml.cs
@@ -8,6 +8,7 @@
using System.Windows;
using System.Windows.Input;
+using FontAwesome.WPF;
using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.UserInterface.Contracts.Windows;
using SafeExamBrowser.UserInterface.Contracts.Windows.Data;
@@ -15,7 +16,7 @@ using SafeExamBrowser.UserInterface.Contracts.Windows.Events;
namespace SafeExamBrowser.UserInterface.Desktop.Windows
{
- public partial class NetworkDialog : Window, INetworkDialog
+ public partial class CredentialsDialog : Window, ICredentialsDialog
{
private readonly IText text;
@@ -34,12 +35,12 @@ namespace SafeExamBrowser.UserInterface.Desktop.Windows
remove { closing -= value; }
}
- internal NetworkDialog(string message, string title, IText text)
+ internal CredentialsDialog(CredentialsDialogPurpose purpose, string message, string title, IText text)
{
this.text = text;
InitializeComponent();
- InitializeNetworkDialog(message, title);
+ InitializeCredentialsDialog(purpose, message, title);
}
public void BringToForeground()
@@ -47,11 +48,11 @@ namespace SafeExamBrowser.UserInterface.Desktop.Windows
Dispatcher.Invoke(Activate);
}
- public NetworkDialogResult Show(IWindow parent = null)
+ public CredentialsDialogResult Show(IWindow parent = null)
{
return Dispatcher.Invoke(() =>
{
- var result = new NetworkDialogResult { Success = false };
+ var result = new CredentialsDialogResult { Success = false };
if (parent is Window)
{
@@ -70,14 +71,24 @@ namespace SafeExamBrowser.UserInterface.Desktop.Windows
});
}
- private void InitializeNetworkDialog(string message, string title)
+ private void InitializeCredentialsDialog(CredentialsDialogPurpose purpose, string message, string title)
{
+ if (purpose == CredentialsDialogPurpose.WirelessNetwork)
+ {
+ PurposeIcon.Icon = FontAwesomeIcon.Wifi;
+ PurposeIcon.Rotation = 0;
+ UsernameLabel.Content = text.Get(TextKey.CredentialsDialog_UsernameOptionalLabel);
+ }
+ else
+ {
+ PurposeIcon.Icon = FontAwesomeIcon.Key;
+ PurposeIcon.Rotation = 90;
+ UsernameLabel.Content = text.Get(TextKey.CredentialsDialog_UsernameLabel);
+ }
+
+ PasswordLabel.Content = text.Get(TextKey.CredentialsDialog_PasswordLabel);
Message.Text = message;
- // TODO
- PasswordLabel.Content = "Password";
Title = title;
- // TODO
- UsernameLabel.Content = "Username";
WindowStartupLocation = WindowStartupLocation.CenterScreen;
Closed += (o, args) => closed?.Invoke();
diff --git a/SafeExamBrowser.UserInterface.Mobile/Controls/ActionCenter/NetworkControl.xaml.cs b/SafeExamBrowser.UserInterface.Mobile/Controls/ActionCenter/NetworkControl.xaml.cs
index bff25c0e..01a86e66 100644
--- a/SafeExamBrowser.UserInterface.Mobile/Controls/ActionCenter/NetworkControl.xaml.cs
+++ b/SafeExamBrowser.UserInterface.Mobile/Controls/ActionCenter/NetworkControl.xaml.cs
@@ -63,6 +63,12 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls.ActionCenter
}
Popup.IsOpen = Popup.IsMouseOver;
}));
+ Popup.Closed += (o, args) =>
+ {
+ adapter.StopWirelessNetworkScanning();
+ Grid.Background = originalBrush;
+ lastOpenedBySpacePress = false;
+ };
Popup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() =>
{
if (Popup.IsOpen && lastOpenedBySpacePress)
@@ -73,6 +79,7 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls.ActionCenter
}));
Popup.Opened += (o, args) =>
{
+ adapter.StartWirelessNetworkScanning();
Grid.Background = Brushes.Gray;
Task.Delay(100).ContinueWith((task) => Dispatcher.Invoke(() =>
{
@@ -82,11 +89,6 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls.ActionCenter
}
}));
};
- Popup.Closed += (o, args) =>
- {
- Grid.Background = originalBrush;
- lastOpenedBySpacePress = false;
- };
WirelessIcon.Child = GetWirelessIcon(0);
Update();
@@ -94,23 +96,6 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls.ActionCenter
private void Update()
{
- WirelessNetworksStackPanel.Children.Clear();
-
- foreach (var network in adapter.GetWirelessNetworks())
- {
- var button = new NetworkButton(network);
-
- button.NetworkSelected += (o, args) => adapter.ConnectToWirelessNetwork(network.Name);
-
- 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:
@@ -154,6 +139,23 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls.ActionCenter
WirelessIcon.Child = GetWirelessIcon(0);
break;
}
+
+ WirelessNetworksStackPanel.Children.Clear();
+
+ foreach (var network in adapter.GetWirelessNetworks())
+ {
+ var button = new NetworkButton(network);
+
+ button.NetworkSelected += (o, args) => adapter.ConnectToWirelessNetwork(network.Name);
+
+ if (network.Status == ConnectionStatus.Connected)
+ {
+ WirelessIcon.Child = GetWirelessIcon(network.SignalStrength);
+ UpdateText(text.Get(TextKey.SystemControl_NetworkWirelessConnected).Replace("%%NAME%%", network.Name));
+ }
+
+ WirelessNetworksStackPanel.Children.Add(button);
+ }
}
private void UpdateText(string text)
diff --git a/SafeExamBrowser.UserInterface.Mobile/Controls/Taskbar/NetworkControl.xaml.cs b/SafeExamBrowser.UserInterface.Mobile/Controls/Taskbar/NetworkControl.xaml.cs
index d18a7768..4d4a9679 100644
--- a/SafeExamBrowser.UserInterface.Mobile/Controls/Taskbar/NetworkControl.xaml.cs
+++ b/SafeExamBrowser.UserInterface.Mobile/Controls/Taskbar/NetworkControl.xaml.cs
@@ -66,6 +66,7 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls.Taskbar
}));
Popup.Closed += (o, args) =>
{
+ adapter.StopWirelessNetworkScanning();
Background = originalBrush;
Button.Background = originalBrush;
lastOpenedBySpacePress = false;
@@ -81,6 +82,7 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls.Taskbar
}));
Popup.Opened += (o, args) =>
{
+ adapter.StartWirelessNetworkScanning();
Background = Brushes.LightGray;
Button.Background = Brushes.LightGray;
Task.Delay(100).ContinueWith((task) => Dispatcher.Invoke(() =>
@@ -106,23 +108,6 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls.Taskbar
private void Update()
{
- WirelessNetworksStackPanel.Children.Clear();
-
- foreach (var network in adapter.GetWirelessNetworks())
- {
- var button = new NetworkButton(network);
-
- button.NetworkSelected += (o, args) => adapter.ConnectToWirelessNetwork(network.Name);
-
- 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:
@@ -166,6 +151,23 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls.Taskbar
WirelessIcon.Child = GetWirelessIcon(0);
break;
}
+
+ WirelessNetworksStackPanel.Children.Clear();
+
+ foreach (var network in adapter.GetWirelessNetworks())
+ {
+ var button = new NetworkButton(network);
+
+ button.NetworkSelected += (o, args) => adapter.ConnectToWirelessNetwork(network.Name);
+
+ if (network.Status == ConnectionStatus.Connected)
+ {
+ WirelessIcon.Child = GetWirelessIcon(network.SignalStrength);
+ UpdateText(text.Get(TextKey.SystemControl_NetworkWirelessConnected).Replace("%%NAME%%", network.Name));
+ }
+
+ WirelessNetworksStackPanel.Children.Add(button);
+ }
}
private void UpdateText(string text)
diff --git a/SafeExamBrowser.UserInterface.Mobile/SafeExamBrowser.UserInterface.Mobile.csproj b/SafeExamBrowser.UserInterface.Mobile/SafeExamBrowser.UserInterface.Mobile.csproj
index 0179498c..f696bb1a 100644
--- a/SafeExamBrowser.UserInterface.Mobile/SafeExamBrowser.UserInterface.Mobile.csproj
+++ b/SafeExamBrowser.UserInterface.Mobile/SafeExamBrowser.UserInterface.Mobile.csproj
@@ -173,6 +173,9 @@
MessageBoxDialog.xaml
+
+ CredentialsDialog.xaml
+
PasswordDialog.xaml
@@ -521,6 +524,10 @@
Designer
MSBuild:Compile
+
+ MSBuild:Compile
+ Designer
+
MSBuild:Compile
Designer
diff --git a/SafeExamBrowser.UserInterface.Mobile/UserInterfaceFactory.cs b/SafeExamBrowser.UserInterface.Mobile/UserInterfaceFactory.cs
index e90a4a52..b67bcc4f 100644
--- a/SafeExamBrowser.UserInterface.Mobile/UserInterfaceFactory.cs
+++ b/SafeExamBrowser.UserInterface.Mobile/UserInterfaceFactory.cs
@@ -85,6 +85,11 @@ namespace SafeExamBrowser.UserInterface.Mobile
return Application.Current.Dispatcher.Invoke(() => new BrowserWindow(control, settings, isMainWindow, text, logger));
}
+ public ICredentialsDialog CreateCredentialsDialog(CredentialsDialogPurpose purpose, string message, string title)
+ {
+ return Application.Current.Dispatcher.Invoke(() => new CredentialsDialog(purpose, message, title, text));
+ }
+
public IExamSelectionDialog CreateExamSelectionDialog(IEnumerable exams)
{
return Application.Current.Dispatcher.Invoke(() => new ExamSelectionDialog(exams, text));
@@ -143,12 +148,6 @@ namespace SafeExamBrowser.UserInterface.Mobile
}
}
- public INetworkDialog CreateNetworkDialog(string message, string title)
- {
- // TODO
- throw new System.NotImplementedException();
- }
-
public INotificationControl CreateNotificationControl(INotification notification, Location location)
{
if (location == Location.ActionCenter)
diff --git a/SafeExamBrowser.UserInterface.Mobile/Windows/CredentialsDialog.xaml b/SafeExamBrowser.UserInterface.Mobile/Windows/CredentialsDialog.xaml
new file mode 100644
index 00000000..684b5c95
--- /dev/null
+++ b/SafeExamBrowser.UserInterface.Mobile/Windows/CredentialsDialog.xaml
@@ -0,0 +1,56 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/SafeExamBrowser.UserInterface.Mobile/Windows/CredentialsDialog.xaml.cs b/SafeExamBrowser.UserInterface.Mobile/Windows/CredentialsDialog.xaml.cs
new file mode 100644
index 00000000..ce9e1caf
--- /dev/null
+++ b/SafeExamBrowser.UserInterface.Mobile/Windows/CredentialsDialog.xaml.cs
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 2024 ETH Zürich, IT Services
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+using System.Windows;
+using System.Windows.Input;
+using FontAwesome.WPF;
+using SafeExamBrowser.I18n.Contracts;
+using SafeExamBrowser.UserInterface.Contracts.Windows;
+using SafeExamBrowser.UserInterface.Contracts.Windows.Data;
+using SafeExamBrowser.UserInterface.Contracts.Windows.Events;
+
+namespace SafeExamBrowser.UserInterface.Mobile.Windows
+{
+ internal partial class CredentialsDialog : Window, ICredentialsDialog
+ {
+ private readonly IText text;
+
+ private WindowClosedEventHandler closed;
+ private WindowClosingEventHandler closing;
+
+ event WindowClosedEventHandler IWindow.Closed
+ {
+ add { closed += value; }
+ remove { closed -= value; }
+ }
+
+ event WindowClosingEventHandler IWindow.Closing
+ {
+ add { closing += value; }
+ remove { closing -= value; }
+ }
+
+ internal CredentialsDialog(CredentialsDialogPurpose purpose, string message, string title, IText text)
+ {
+ this.text = text;
+
+ InitializeComponent();
+ InitializeCredentialsDialog(purpose, message, title);
+ }
+
+ public void BringToForeground()
+ {
+ Dispatcher.Invoke(Activate);
+ }
+
+ public CredentialsDialogResult Show(IWindow parent = null)
+ {
+ return Dispatcher.Invoke(() =>
+ {
+ var result = new CredentialsDialogResult { Success = false };
+
+ if (parent is Window)
+ {
+ Owner = parent as Window;
+ }
+
+ InitializeBounds();
+
+ if (ShowDialog() is true)
+ {
+ result.Password = Password.Password;
+ result.Success = true;
+ result.Username = Username.Text;
+ }
+
+ return result;
+ });
+ }
+
+ private void InitializeBounds()
+ {
+ Left = 0;
+ Top = 0;
+ Height = SystemParameters.PrimaryScreenHeight;
+ Width = SystemParameters.PrimaryScreenWidth;
+ }
+
+ private void InitializeCredentialsDialog(CredentialsDialogPurpose purpose, string message, string title)
+ {
+ InitializeBounds();
+
+ if (purpose == CredentialsDialogPurpose.WirelessNetwork)
+ {
+ PurposeIcon.Icon = FontAwesomeIcon.Wifi;
+ PurposeIcon.Rotation = 0;
+ UsernameLabel.Content = text.Get(TextKey.CredentialsDialog_UsernameOptionalLabel);
+ }
+ else
+ {
+ PurposeIcon.Icon = FontAwesomeIcon.Key;
+ PurposeIcon.Rotation = 90;
+ UsernameLabel.Content = text.Get(TextKey.CredentialsDialog_UsernameLabel);
+ }
+
+ PasswordLabel.Content = text.Get(TextKey.CredentialsDialog_PasswordLabel);
+ Message.Text = message;
+ Title = title;
+
+ Closed += (o, args) => closed?.Invoke();
+ Closing += (o, args) => closing?.Invoke();
+ Loaded += (o, args) => Activate();
+
+ CancelButton.Content = text.Get(TextKey.PasswordDialog_Cancel);
+ CancelButton.Click += CancelButton_Click;
+
+ ConfirmButton.Content = text.Get(TextKey.PasswordDialog_Confirm);
+ ConfirmButton.Click += ConfirmButton_Click;
+
+ Password.KeyDown += Password_KeyDown;
+
+ SystemParameters.StaticPropertyChanged += SystemParameters_StaticPropertyChanged;
+ }
+
+ private void CancelButton_Click(object sender, RoutedEventArgs e)
+ {
+ DialogResult = false;
+ Close();
+ }
+
+ private void ConfirmButton_Click(object sender, RoutedEventArgs e)
+ {
+ DialogResult = true;
+ Close();
+ }
+
+ private void Password_KeyDown(object sender, KeyEventArgs e)
+ {
+ if (e.Key == Key.Enter)
+ {
+ DialogResult = true;
+ Close();
+ }
+ }
+
+ private void SystemParameters_StaticPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
+ {
+ if (e.PropertyName == nameof(SystemParameters.WorkArea))
+ {
+ Dispatcher.InvokeAsync(InitializeBounds);
+ }
+ }
+ }
+}
diff --git a/SafeExamBrowser.sln b/SafeExamBrowser.sln
index 8c7943cd..b36bb796 100644
--- a/SafeExamBrowser.sln
+++ b/SafeExamBrowser.sln
@@ -27,11 +27,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SafeExamBrowser.SystemCompo
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SafeExamBrowser.UserInterface.Desktop", "SafeExamBrowser.UserInterface.Desktop\SafeExamBrowser.UserInterface.Desktop.csproj", "{A502DF54-7169-4647-94BD-18B192924866}"
EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Libraries", "Libraries", "{E03B19EC-8E4D-481C-9C1B-5C6C060D3D6B}"
- ProjectSection(SolutionItems) = preProject
- Libraries\SimpleWifi.dll = Libraries\SimpleWifi.dll
- EndProjectSection
-EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SafeExamBrowser.Client", "SafeExamBrowser.Client\SafeExamBrowser.Client.csproj", "{7CC5A895-E0D3-4E43-9B39-CCEC05A5A6A7}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SafeExamBrowser.Client.UnitTests", "SafeExamBrowser.Client.UnitTests\SafeExamBrowser.Client.UnitTests.csproj", "{15684416-FADF-4C51-85DE-4F343BFAB752}"