diff --git a/SafeExamBrowser.Client/ClientContext.cs b/SafeExamBrowser.Client/ClientContext.cs
index 422eb0f3..901bf1e7 100644
--- a/SafeExamBrowser.Client/ClientContext.cs
+++ b/SafeExamBrowser.Client/ClientContext.cs
@@ -12,6 +12,7 @@ using SafeExamBrowser.Applications.Contracts;
using SafeExamBrowser.Browser.Contracts;
using SafeExamBrowser.Communication.Contracts.Hosts;
using SafeExamBrowser.Configuration.Contracts;
+using SafeExamBrowser.Server.Contracts;
using SafeExamBrowser.Settings;
using SafeExamBrowser.UserInterface.Contracts.Shell;
@@ -47,6 +48,11 @@ namespace SafeExamBrowser.Client
///
internal IClientHost ClientHost { get; set; }
+ ///
+ /// The server proxy, if the current session mode is .
+ ///
+ internal IServerProxy Server { get; set; }
+
///
/// The identifier of the current session.
///
diff --git a/SafeExamBrowser.Client/ClientController.cs b/SafeExamBrowser.Client/ClientController.cs
index ddcb55e0..86ca9047 100644
--- a/SafeExamBrowser.Client/ClientController.cs
+++ b/SafeExamBrowser.Client/ClientController.cs
@@ -26,6 +26,7 @@ using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.Monitoring.Contracts.Applications;
using SafeExamBrowser.Monitoring.Contracts.Display;
using SafeExamBrowser.Monitoring.Contracts.System;
+using SafeExamBrowser.Server.Contracts;
using SafeExamBrowser.Settings;
using SafeExamBrowser.UserInterface.Contracts;
using SafeExamBrowser.UserInterface.Contracts.FileSystemDialog;
@@ -60,6 +61,7 @@ namespace SafeExamBrowser.Client
private IBrowserApplication Browser => context.Browser;
private IClientHost ClientHost => context.ClientHost;
+ private IServerProxy Server => context.Server;
private AppSettings Settings => context.Settings;
internal ClientController(
@@ -196,6 +198,11 @@ namespace SafeExamBrowser.Client
{
activator.Activated += TerminationActivator_Activated;
}
+
+ if (Server != null)
+ {
+ // TODO
+ }
}
private void DeregisterEvents()
@@ -222,6 +229,11 @@ namespace SafeExamBrowser.Client
ClientHost.Shutdown -= ClientHost_Shutdown;
}
+ if (Server != null)
+ {
+ // TODO
+ }
+
foreach (var activator in context.Activators.OfType())
{
activator.Activated -= TerminationActivator_Activated;
diff --git a/SafeExamBrowser.Client/CompositionRoot.cs b/SafeExamBrowser.Client/CompositionRoot.cs
index 3a885aee..34936310 100644
--- a/SafeExamBrowser.Client/CompositionRoot.cs
+++ b/SafeExamBrowser.Client/CompositionRoot.cs
@@ -31,6 +31,7 @@ using SafeExamBrowser.Monitoring.Display;
using SafeExamBrowser.Monitoring.Keyboard;
using SafeExamBrowser.Monitoring.Mouse;
using SafeExamBrowser.Monitoring.System;
+using SafeExamBrowser.Server;
using SafeExamBrowser.Settings.Logging;
using SafeExamBrowser.Settings.UserInterface;
using SafeExamBrowser.SystemComponents;
@@ -118,6 +119,7 @@ namespace SafeExamBrowser.Client
operations.Enqueue(new SystemMonitorOperation(context, systemMonitor, logger));
operations.Enqueue(new LazyInitializationOperation(BuildShellOperation));
operations.Enqueue(new LazyInitializationOperation(BuildBrowserOperation));
+ operations.Enqueue(new LazyInitializationOperation(BuildServerOperation));
operations.Enqueue(new ClipboardOperation(context, logger, nativeMethods));
var sequence = new OperationSequence(logger, operations);
@@ -237,6 +239,14 @@ namespace SafeExamBrowser.Client
return operation;
}
+ private IOperation BuildServerOperation()
+ {
+ var server = new ServerProxy(context.AppConfig, logger);
+ var operation = new ServerOperation(actionCenter, context, logger, server, taskbar);
+
+ return operation;
+ }
+
private IOperation BuildShellOperation()
{
var aboutInfo = new AboutNotificationInfo(text);
diff --git a/SafeExamBrowser.Client/Operations/ServerOperation.cs b/SafeExamBrowser.Client/Operations/ServerOperation.cs
new file mode 100644
index 00000000..189930d3
--- /dev/null
+++ b/SafeExamBrowser.Client/Operations/ServerOperation.cs
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2020 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 SafeExamBrowser.Core.Contracts.OperationModel;
+using SafeExamBrowser.Core.Contracts.OperationModel.Events;
+using SafeExamBrowser.I18n.Contracts;
+using SafeExamBrowser.Logging.Contracts;
+using SafeExamBrowser.Server.Contracts;
+using SafeExamBrowser.Settings;
+using SafeExamBrowser.UserInterface.Contracts.Shell;
+
+namespace SafeExamBrowser.Client.Operations
+{
+ internal class ServerOperation : ClientOperation
+ {
+ private readonly IActionCenter actionCenter;
+ private readonly ILogger logger;
+ private readonly IServerProxy server;
+ private readonly ITaskbar taskbar;
+
+ public override event ActionRequiredEventHandler ActionRequired { add { } remove { } }
+ public override event StatusChangedEventHandler StatusChanged;
+
+ public ServerOperation(
+ IActionCenter actionCenter,
+ ClientContext context,
+ ILogger logger,
+ IServerProxy server,
+ ITaskbar taskbar) : base(context)
+ {
+ this.actionCenter = actionCenter;
+ this.logger = logger;
+ this.server = server;
+ this.taskbar = taskbar;
+ }
+
+ public override OperationResult Perform()
+ {
+ var result = OperationResult.Success;
+
+ if (Context.Settings.SessionMode == SessionMode.Server)
+ {
+ logger.Info("Initializing server...");
+ StatusChanged?.Invoke(TextKey.OperationStatus_InitializeServer);
+
+ server.Initialize(Context.AppConfig.ServerApi, Context.AppConfig.ServerConnectionToken, Context.AppConfig.ServerOauth2Token, Context.Settings.Server);
+
+ // TODO: Add action center and taskbar notifications
+ }
+
+ return result;
+ }
+
+ public override OperationResult Revert()
+ {
+ var result = OperationResult.Success;
+
+ if (Context.Settings?.SessionMode == SessionMode.Server)
+ {
+ logger.Info("Finalizing server...");
+ StatusChanged?.Invoke(TextKey.OperationStatus_FinalizeServer);
+
+ // TODO: Stop sending pings and logs (or in controller?)
+ // TODO: Stop action center and taskbar notifications
+ }
+
+ return result;
+ }
+ }
+}
diff --git a/SafeExamBrowser.Client/SafeExamBrowser.Client.csproj b/SafeExamBrowser.Client/SafeExamBrowser.Client.csproj
index 3d63f785..00a9545e 100644
--- a/SafeExamBrowser.Client/SafeExamBrowser.Client.csproj
+++ b/SafeExamBrowser.Client/SafeExamBrowser.Client.csproj
@@ -94,6 +94,7 @@
+
@@ -193,6 +194,14 @@
{EF563531-4EB5-44B9-A5EC-D6D6F204469B}
SafeExamBrowser.Monitoring
+
+ {db701e6f-bddc-4cec-b662-335a9dc11809}
+ SafeExamBrowser.Server.Contracts
+
+
+ {46edbde0-58b4-4725-9783-0c55c3d49c0c}
+ SafeExamBrowser.Server
+
{30b2d907-5861-4f39-abad-c4abf1b3470e}
SafeExamBrowser.Settings
diff --git a/SafeExamBrowser.Configuration.Contracts/AppConfig.cs b/SafeExamBrowser.Configuration.Contracts/AppConfig.cs
index f7d68a57..245f4608 100644
--- a/SafeExamBrowser.Configuration.Contracts/AppConfig.cs
+++ b/SafeExamBrowser.Configuration.Contracts/AppConfig.cs
@@ -151,6 +151,21 @@ namespace SafeExamBrowser.Configuration.Contracts
///
public string SebUriSchemeSecure { get; set; }
+ ///
+ /// The server API as JSON string.
+ ///
+ public string ServerApi { get; set; }
+
+ ///
+ /// The connection token for a server.
+ ///
+ public string ServerConnectionToken { get; set; }
+
+ ///
+ /// The OAuth2 token for a server.
+ ///
+ public string ServerOauth2Token { get; set; }
+
///
/// The communication address of the service component.
///
diff --git a/SafeExamBrowser.Runtime/Operations/Events/ExamSelectionEventArgs.cs b/SafeExamBrowser.Runtime/Operations/Events/ExamSelectionEventArgs.cs
index d469ec0b..9089ad6c 100644
--- a/SafeExamBrowser.Runtime/Operations/Events/ExamSelectionEventArgs.cs
+++ b/SafeExamBrowser.Runtime/Operations/Events/ExamSelectionEventArgs.cs
@@ -8,7 +8,7 @@
using System.Collections.Generic;
using SafeExamBrowser.Core.Contracts.OperationModel.Events;
-using SafeExamBrowser.Server.Contracts;
+using SafeExamBrowser.Server.Contracts.Data;
namespace SafeExamBrowser.Runtime.Operations.Events
{
diff --git a/SafeExamBrowser.Runtime/Operations/ServerOperation.cs b/SafeExamBrowser.Runtime/Operations/ServerOperation.cs
index 68eb0aed..cdac4bda 100644
--- a/SafeExamBrowser.Runtime/Operations/ServerOperation.cs
+++ b/SafeExamBrowser.Runtime/Operations/ServerOperation.cs
@@ -15,6 +15,7 @@ using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.Runtime.Operations.Events;
using SafeExamBrowser.Server.Contracts;
+using SafeExamBrowser.Server.Contracts.Data;
using SafeExamBrowser.Settings;
using SafeExamBrowser.SystemComponents.Contracts;
@@ -53,7 +54,7 @@ namespace SafeExamBrowser.Runtime.Operations
server.Initialize(Context.Next.Settings.Server);
- var (abort, fallback, success) = TryPerformWithFallback(() => server.Connect(), out var token);
+ var (abort, fallback, success) = TryPerformWithFallback(() => server.Connect());
if (success)
{
@@ -69,12 +70,16 @@ namespace SafeExamBrowser.Runtime.Operations
if (success)
{
+ var info = server.GetConnectionInfo();
var status = TryLoadSettings(uri, UriSource.Server, out _, out var settings);
fileSystem.Delete(uri.LocalPath);
if (status == LoadStatus.Success)
{
+ Context.Next.AppConfig.ServerApi = info.Api;
+ Context.Next.AppConfig.ServerConnectionToken = info.ConnectionToken;
+ Context.Next.AppConfig.ServerOauth2Token = info.Oauth2Token;
Context.Next.Settings = settings;
Context.Next.Settings.Browser.StartUrl = exam.Url;
result = OperationResult.Success;
@@ -121,7 +126,7 @@ namespace SafeExamBrowser.Runtime.Operations
public override OperationResult Revert()
{
- var result = OperationResult.Failed;
+ var result = OperationResult.Success;
if (Context.Current?.Settings.SessionMode == SessionMode.Server)
{
@@ -139,10 +144,6 @@ namespace SafeExamBrowser.Runtime.Operations
result = OperationResult.Failed;
}
}
- else
- {
- result = OperationResult.Success;
- }
return result;
}
diff --git a/SafeExamBrowser.Server.Contracts/Data/ConnectionInfo.cs b/SafeExamBrowser.Server.Contracts/Data/ConnectionInfo.cs
new file mode 100644
index 00000000..1f9cc7ed
--- /dev/null
+++ b/SafeExamBrowser.Server.Contracts/Data/ConnectionInfo.cs
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 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.Server.Contracts.Data
+{
+ ///
+ /// Contains all information required to establish a connection with a server.
+ ///
+ public class ConnectionInfo
+ {
+ ///
+ /// The API of the server as JSON string.
+ ///
+ public string Api { get; set; }
+
+ ///
+ /// The connection token for authentication with the server.
+ ///
+ public string ConnectionToken { get; set; }
+
+ ///
+ /// The OAuth2 token for authentication with the server.
+ ///
+ public string Oauth2Token { get; set; }
+ }
+}
diff --git a/SafeExamBrowser.Server.Contracts/Exam.cs b/SafeExamBrowser.Server.Contracts/Data/Exam.cs
similarity index 94%
rename from SafeExamBrowser.Server.Contracts/Exam.cs
rename to SafeExamBrowser.Server.Contracts/Data/Exam.cs
index de303948..3d93608d 100644
--- a/SafeExamBrowser.Server.Contracts/Exam.cs
+++ b/SafeExamBrowser.Server.Contracts/Data/Exam.cs
@@ -6,7 +6,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
-namespace SafeExamBrowser.Server.Contracts
+namespace SafeExamBrowser.Server.Contracts.Data
{
///
/// Defines a server exam.
diff --git a/SafeExamBrowser.Server.Contracts/ServerResponse.cs b/SafeExamBrowser.Server.Contracts/Data/ServerResponse.cs
similarity index 96%
rename from SafeExamBrowser.Server.Contracts/ServerResponse.cs
rename to SafeExamBrowser.Server.Contracts/Data/ServerResponse.cs
index 6b7fe155..5f9b4631 100644
--- a/SafeExamBrowser.Server.Contracts/ServerResponse.cs
+++ b/SafeExamBrowser.Server.Contracts/Data/ServerResponse.cs
@@ -6,7 +6,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
-namespace SafeExamBrowser.Server.Contracts
+namespace SafeExamBrowser.Server.Contracts.Data
{
///
/// Defines the result of a communication with a SEB server.
diff --git a/SafeExamBrowser.Server.Contracts/IServerProxy.cs b/SafeExamBrowser.Server.Contracts/IServerProxy.cs
index 157efcdb..eb779e1f 100644
--- a/SafeExamBrowser.Server.Contracts/IServerProxy.cs
+++ b/SafeExamBrowser.Server.Contracts/IServerProxy.cs
@@ -8,6 +8,7 @@
using System;
using System.Collections.Generic;
+using SafeExamBrowser.Server.Contracts.Data;
using SafeExamBrowser.Settings.Server;
namespace SafeExamBrowser.Server.Contracts
@@ -18,33 +19,42 @@ namespace SafeExamBrowser.Server.Contracts
public interface IServerProxy
{
///
- /// TODO: Return API as well or re-load in proxy instance of client?
- /// Attempts to initialize a connection to the server. If successful, returns a OAuth2 token as response value.
+ /// Attempts to initialize a connection to the server.
///
- ServerResponse Connect();
+ ServerResponse Connect();
///
- ///
+ /// TODO
///
ServerResponse Disconnect();
///
- ///
+ /// Retrieves a list of all currently available exams.
///
ServerResponse> GetAvailableExams();
///
- ///
+ /// Retrieves the URI of the configuration file for the given exam.
///
ServerResponse GetConfigurationFor(Exam exam);
+ ///
+ /// Retrieves the information required to establish a connection with this server.
+ ///
+ ConnectionInfo GetConnectionInfo();
+
///
/// Initializes the server settings to be used for communication.
///
void Initialize(ServerSettings settings);
///
- ///
+ /// Initializes the configuration and server settings to be used for communication.
+ ///
+ void Initialize(string api, string connectionToken, string oauth2Token, ServerSettings settings);
+
+ ///
+ /// TODO
///
ServerResponse SendSessionInfo(string sessionId);
}
diff --git a/SafeExamBrowser.Server.Contracts/SafeExamBrowser.Server.Contracts.csproj b/SafeExamBrowser.Server.Contracts/SafeExamBrowser.Server.Contracts.csproj
index 9cf9979c..b5e6086e 100644
--- a/SafeExamBrowser.Server.Contracts/SafeExamBrowser.Server.Contracts.csproj
+++ b/SafeExamBrowser.Server.Contracts/SafeExamBrowser.Server.Contracts.csproj
@@ -54,10 +54,11 @@
-
+
+
-
+
diff --git a/SafeExamBrowser.Server/Data/ApiVersion1.cs b/SafeExamBrowser.Server/Data/ApiVersion1.cs
index 23df1ca8..3d723fde 100644
--- a/SafeExamBrowser.Server/Data/ApiVersion1.cs
+++ b/SafeExamBrowser.Server/Data/ApiVersion1.cs
@@ -11,13 +11,9 @@ namespace SafeExamBrowser.Server.Data
internal class ApiVersion1
{
public string AccessTokenEndpoint { get; set; }
-
public string HandshakeEndpoint { get; set; }
-
public string ConfigurationEndpoint { get; set; }
-
public string PingEndpoint { get; set; }
-
public string LogEndpoint { get; set; }
}
}
diff --git a/SafeExamBrowser.Server/ServerProxy.cs b/SafeExamBrowser.Server/ServerProxy.cs
index 0bf55d62..9e2c24a0 100644
--- a/SafeExamBrowser.Server/ServerProxy.cs
+++ b/SafeExamBrowser.Server/ServerProxy.cs
@@ -19,6 +19,7 @@ using Newtonsoft.Json.Linq;
using SafeExamBrowser.Configuration.Contracts;
using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.Server.Contracts;
+using SafeExamBrowser.Server.Contracts.Data;
using SafeExamBrowser.Server.Data;
using SafeExamBrowser.Settings.Server;
@@ -42,7 +43,7 @@ namespace SafeExamBrowser.Server
this.logger = logger;
}
- public ServerResponse Connect()
+ public ServerResponse Connect()
{
var success = TryExecute(HttpMethod.Get, settings.ApiUrl, out var response);
var message = ToString(response);
@@ -73,7 +74,7 @@ namespace SafeExamBrowser.Server
logger.Error("Failed to load server API!");
}
- return new ServerResponse(success, oauth2Token, message);
+ return new ServerResponse(success, message);
}
public ServerResponse Disconnect()
@@ -151,6 +152,16 @@ namespace SafeExamBrowser.Server
return new ServerResponse(success, uri, message);
}
+ public ConnectionInfo GetConnectionInfo()
+ {
+ return new ConnectionInfo
+ {
+ Api = JsonConvert.SerializeObject(api),
+ ConnectionToken = connectionToken,
+ Oauth2Token = oauth2Token
+ };
+ }
+
public void Initialize(ServerSettings settings)
{
this.settings = settings;
@@ -162,6 +173,15 @@ namespace SafeExamBrowser.Server
}
}
+ public void Initialize(string api, string connectionToken, string oauth2Token, ServerSettings settings)
+ {
+ this.api = JsonConvert.DeserializeObject(api);
+ this.connectionToken = connectionToken;
+ this.oauth2Token = oauth2Token;
+
+ Initialize(settings);
+ }
+
public ServerResponse SendSessionInfo(string sessionId)
{
return new ServerResponse(false, "TODO!");
@@ -324,7 +344,7 @@ namespace SafeExamBrowser.Server
}
catch (TaskCanceledException)
{
- logger.Error($"Request {request.Method} '{request.RequestUri}' did not complete within {settings.RequestTimeout}ms!");
+ logger.Debug($"Request {request.Method} '{request.RequestUri}' did not complete within {settings.RequestTimeout}ms!");
break;
}
catch (Exception e)
diff --git a/SafeExamBrowser.UserInterface.Contracts/IUserInterfaceFactory.cs b/SafeExamBrowser.UserInterface.Contracts/IUserInterfaceFactory.cs
index 70418550..08d648f2 100644
--- a/SafeExamBrowser.UserInterface.Contracts/IUserInterfaceFactory.cs
+++ b/SafeExamBrowser.UserInterface.Contracts/IUserInterfaceFactory.cs
@@ -12,7 +12,7 @@ using SafeExamBrowser.Client.Contracts;
using SafeExamBrowser.Configuration.Contracts;
using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.Logging.Contracts;
-using SafeExamBrowser.Server.Contracts;
+using SafeExamBrowser.Server.Contracts.Data;
using SafeExamBrowser.Settings.Browser;
using SafeExamBrowser.SystemComponents.Contracts.Audio;
using SafeExamBrowser.SystemComponents.Contracts.Keyboard;
diff --git a/SafeExamBrowser.UserInterface.Contracts/Windows/Data/ExamSelectionDialogResult.cs b/SafeExamBrowser.UserInterface.Contracts/Windows/Data/ExamSelectionDialogResult.cs
index db4190bf..7c9cf931 100644
--- a/SafeExamBrowser.UserInterface.Contracts/Windows/Data/ExamSelectionDialogResult.cs
+++ b/SafeExamBrowser.UserInterface.Contracts/Windows/Data/ExamSelectionDialogResult.cs
@@ -6,7 +6,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
-using SafeExamBrowser.Server.Contracts;
+using SafeExamBrowser.Server.Contracts.Data;
namespace SafeExamBrowser.UserInterface.Contracts.Windows.Data
{
diff --git a/SafeExamBrowser.UserInterface.Desktop/UserInterfaceFactory.cs b/SafeExamBrowser.UserInterface.Desktop/UserInterfaceFactory.cs
index 41fde627..e1d1c89a 100644
--- a/SafeExamBrowser.UserInterface.Desktop/UserInterfaceFactory.cs
+++ b/SafeExamBrowser.UserInterface.Desktop/UserInterfaceFactory.cs
@@ -16,7 +16,7 @@ using SafeExamBrowser.Client.Contracts;
using SafeExamBrowser.Configuration.Contracts;
using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.Logging.Contracts;
-using SafeExamBrowser.Server.Contracts;
+using SafeExamBrowser.Server.Contracts.Data;
using SafeExamBrowser.Settings.Browser;
using SafeExamBrowser.SystemComponents.Contracts.Audio;
using SafeExamBrowser.SystemComponents.Contracts.Keyboard;
diff --git a/SafeExamBrowser.UserInterface.Desktop/Windows/ExamSelectionDialog.xaml.cs b/SafeExamBrowser.UserInterface.Desktop/Windows/ExamSelectionDialog.xaml.cs
index 5e31bb55..7d24f079 100644
--- a/SafeExamBrowser.UserInterface.Desktop/Windows/ExamSelectionDialog.xaml.cs
+++ b/SafeExamBrowser.UserInterface.Desktop/Windows/ExamSelectionDialog.xaml.cs
@@ -10,7 +10,7 @@ using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using SafeExamBrowser.I18n.Contracts;
-using SafeExamBrowser.Server.Contracts;
+using SafeExamBrowser.Server.Contracts.Data;
using SafeExamBrowser.UserInterface.Contracts.Windows;
using SafeExamBrowser.UserInterface.Contracts.Windows.Data;
diff --git a/SafeExamBrowser.UserInterface.Mobile/UserInterfaceFactory.cs b/SafeExamBrowser.UserInterface.Mobile/UserInterfaceFactory.cs
index 58e05475..6c09dd43 100644
--- a/SafeExamBrowser.UserInterface.Mobile/UserInterfaceFactory.cs
+++ b/SafeExamBrowser.UserInterface.Mobile/UserInterfaceFactory.cs
@@ -16,7 +16,7 @@ using SafeExamBrowser.Client.Contracts;
using SafeExamBrowser.Configuration.Contracts;
using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.Logging.Contracts;
-using SafeExamBrowser.Server.Contracts;
+using SafeExamBrowser.Server.Contracts.Data;
using SafeExamBrowser.Settings.Browser;
using SafeExamBrowser.SystemComponents.Contracts.Audio;
using SafeExamBrowser.SystemComponents.Contracts.Keyboard;