diff --git a/SafeExamBrowser.Applications.Contracts/IApplicationInfo.cs b/SafeExamBrowser.Applications.Contracts/ApplicationInfo.cs
similarity index 82%
rename from SafeExamBrowser.Applications.Contracts/IApplicationInfo.cs
rename to SafeExamBrowser.Applications.Contracts/ApplicationInfo.cs
index cf147822..b47e66f1 100644
--- a/SafeExamBrowser.Applications.Contracts/IApplicationInfo.cs
+++ b/SafeExamBrowser.Applications.Contracts/ApplicationInfo.cs
@@ -13,21 +13,21 @@ namespace SafeExamBrowser.Applications.Contracts
///
/// The information about an application which can be accessed via the shell.
///
- public interface IApplicationInfo
+ public class ApplicationInfo
{
///
/// The name of the application.
///
- string Name { get; }
+ public string Name { get; set; }
///
/// The tooltip for the application.
///
- string Tooltip { get; }
+ public string Tooltip { get; set; }
///
/// The resource providing the application icon.
///
- IIconResource IconResource { get; }
+ public IconResource IconResource { get; set; }
}
}
diff --git a/SafeExamBrowser.Applications.Contracts/Events/IconChangedEventHandler.cs b/SafeExamBrowser.Applications.Contracts/Events/IconChangedEventHandler.cs
index 1376c40a..93c764bc 100644
--- a/SafeExamBrowser.Applications.Contracts/Events/IconChangedEventHandler.cs
+++ b/SafeExamBrowser.Applications.Contracts/Events/IconChangedEventHandler.cs
@@ -13,5 +13,5 @@ namespace SafeExamBrowser.Applications.Contracts.Events
///
/// Event handler used to indicate that the icon of an has changed.
///
- public delegate void IconChangedEventHandler(IIconResource icon);
+ public delegate void IconChangedEventHandler(IconResource icon);
}
diff --git a/SafeExamBrowser.Applications.Contracts/IApplication.cs b/SafeExamBrowser.Applications.Contracts/IApplication.cs
index 7b471329..c75b4908 100644
--- a/SafeExamBrowser.Applications.Contracts/IApplication.cs
+++ b/SafeExamBrowser.Applications.Contracts/IApplication.cs
@@ -18,7 +18,7 @@ namespace SafeExamBrowser.Applications.Contracts
///
/// Provides information about the application.
///
- IApplicationInfo Info { get; }
+ ApplicationInfo Info { get; }
///
/// Fired when a new has started.
diff --git a/SafeExamBrowser.Applications.Contracts/SafeExamBrowser.Applications.Contracts.csproj b/SafeExamBrowser.Applications.Contracts/SafeExamBrowser.Applications.Contracts.csproj
index 88aa494b..d37ed27a 100644
--- a/SafeExamBrowser.Applications.Contracts/SafeExamBrowser.Applications.Contracts.csproj
+++ b/SafeExamBrowser.Applications.Contracts/SafeExamBrowser.Applications.Contracts.csproj
@@ -60,7 +60,7 @@
-
+
diff --git a/SafeExamBrowser.Applications/ApplicationFactory.cs b/SafeExamBrowser.Applications/ApplicationFactory.cs
index 92da83c6..2d77946c 100644
--- a/SafeExamBrowser.Applications/ApplicationFactory.cs
+++ b/SafeExamBrowser.Applications/ApplicationFactory.cs
@@ -11,6 +11,7 @@ using System.Collections.Generic;
using System.IO;
using Microsoft.Win32;
using SafeExamBrowser.Applications.Contracts;
+using SafeExamBrowser.Core.Contracts;
using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.Settings.Applications;
@@ -27,33 +28,46 @@ namespace SafeExamBrowser.Applications
public FactoryResult TryCreate(WhitelistApplication settings, out IApplication application)
{
+ var name = $"'{settings.DisplayName}' ({ settings.ExecutableName})";
+
application = default(IApplication);
try
{
- var success = TryFindMainExecutable(settings, out var mainExecutable);
+ var success = TryFindApplication(settings, out var executablePath);
if (success)
{
- application = new ExternalApplication();
- logger.Debug($"Successfully initialized application '{settings.DisplayName}' ({settings.ExecutableName}).");
+ application = BuildApplication(executablePath, settings);
+ application.Initialize();
+
+ logger.Debug($"Successfully initialized application {name}.");
return FactoryResult.Success;
}
- logger.Error($"Could not find application '{settings.DisplayName}' ({settings.ExecutableName})!");
+ logger.Error($"Could not find application {name}!");
return FactoryResult.NotFound;
}
catch (Exception e)
{
- logger.Error($"Unexpected error while trying to create application '{settings.DisplayName}' ({settings.ExecutableName})!", e);
+ logger.Error($"Unexpected error while trying to initialize application {name}!", e);
}
return FactoryResult.Error;
}
- private bool TryFindMainExecutable(WhitelistApplication settings, out string mainExecutable)
+ private IApplication BuildApplication(string executablePath, WhitelistApplication settings)
+ {
+ var icon = new IconResource { Type = IconResourceType.Embedded, Uri = new Uri(executablePath) };
+ var info = new ApplicationInfo { IconResource = icon, Name = settings.DisplayName, Tooltip = settings.Description ?? settings.DisplayName };
+ var application = new ExternalApplication(executablePath, info);
+
+ return application;
+ }
+
+ private bool TryFindApplication(WhitelistApplication settings, out string mainExecutable)
{
var paths = new List();
var registryPath = QueryPathFromRegistry(settings);
diff --git a/SafeExamBrowser.Applications/ExternalApplication.cs b/SafeExamBrowser.Applications/ExternalApplication.cs
index 8136d4a5..08392f3b 100644
--- a/SafeExamBrowser.Applications/ExternalApplication.cs
+++ b/SafeExamBrowser.Applications/ExternalApplication.cs
@@ -6,7 +6,6 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
-using System;
using SafeExamBrowser.Applications.Contracts;
using SafeExamBrowser.Applications.Contracts.Events;
@@ -14,10 +13,18 @@ namespace SafeExamBrowser.Applications
{
internal class ExternalApplication : IApplication
{
- public IApplicationInfo Info => throw new NotImplementedException();
+ private string executablePath;
+
+ public ApplicationInfo Info { get; }
public event InstanceStartedEventHandler InstanceStarted;
+ internal ExternalApplication(string executablePath, ApplicationInfo info)
+ {
+ this.executablePath = executablePath;
+ this.Info = info;
+ }
+
public void Initialize()
{
diff --git a/SafeExamBrowser.Applications/SafeExamBrowser.Applications.csproj b/SafeExamBrowser.Applications/SafeExamBrowser.Applications.csproj
index 3f81c3cf..0ec08b64 100644
--- a/SafeExamBrowser.Applications/SafeExamBrowser.Applications.csproj
+++ b/SafeExamBrowser.Applications/SafeExamBrowser.Applications.csproj
@@ -62,6 +62,10 @@
{ac77745d-3b41-43e2-8e84-d40e5a4ee77f}
SafeExamBrowser.Applications.Contracts
+
+ {fe0e1224-b447-4b14-81e7-ed7d84822aa0}
+ SafeExamBrowser.Core.Contracts
+
{64ea30fb-11d4-436a-9c2b-88566285363e}
SafeExamBrowser.Logging.Contracts
diff --git a/SafeExamBrowser.Browser/BrowserApplication.cs b/SafeExamBrowser.Browser/BrowserApplication.cs
index 8b1d8dd9..45af2b62 100644
--- a/SafeExamBrowser.Browser/BrowserApplication.cs
+++ b/SafeExamBrowser.Browser/BrowserApplication.cs
@@ -38,7 +38,7 @@ namespace SafeExamBrowser.Browser
private IText text;
private IUserInterfaceFactory uiFactory;
- public IApplicationInfo Info { get; private set; }
+ public ApplicationInfo Info { get; private set; }
public event DownloadRequestedEventHandler ConfigurationDownloadRequested;
public event InstanceStartedEventHandler InstanceStarted;
@@ -65,7 +65,7 @@ namespace SafeExamBrowser.Browser
var cefSettings = InitializeCefSettings();
var success = Cef.Initialize(cefSettings, true, default(IApp));
- Info = new BrowserApplicationInfo();
+ Info = BuildApplicationInfo();
if (success)
{
@@ -95,6 +95,16 @@ namespace SafeExamBrowser.Browser
logger.Info("Terminated browser.");
}
+ private ApplicationInfo BuildApplicationInfo()
+ {
+ return new ApplicationInfo
+ {
+ IconResource = new BrowserIconResource(),
+ Name = "Safe Exam Browser",
+ Tooltip = text.Get(TextKey.Browser_Tooltip)
+ };
+ }
+
private void CreateNewInstance(string url = null)
{
var id = new BrowserInstanceIdentifier(++instanceIdCounter);
diff --git a/SafeExamBrowser.Browser/BrowserApplicationInfo.cs b/SafeExamBrowser.Browser/BrowserApplicationInfo.cs
deleted file mode 100644
index 5639cfb6..00000000
--- a/SafeExamBrowser.Browser/BrowserApplicationInfo.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET)
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- */
-
-using SafeExamBrowser.Applications.Contracts;
-using SafeExamBrowser.Core.Contracts;
-
-namespace SafeExamBrowser.Browser
-{
- public class BrowserApplicationInfo : IApplicationInfo
- {
- public string Name => "Safe Exam Browser";
- public string Tooltip => Name;
- public IIconResource IconResource { get; } = new BrowserIconResource();
- }
-}
diff --git a/SafeExamBrowser.Browser/BrowserIconResource.cs b/SafeExamBrowser.Browser/BrowserIconResource.cs
index 9e07b06d..61f16d88 100644
--- a/SafeExamBrowser.Browser/BrowserIconResource.cs
+++ b/SafeExamBrowser.Browser/BrowserIconResource.cs
@@ -11,14 +11,11 @@ using SafeExamBrowser.Core.Contracts;
namespace SafeExamBrowser.Browser
{
- public class BrowserIconResource : IIconResource
+ public class BrowserIconResource : IconResource
{
- public Uri Uri { get; private set; }
- public bool IsBitmapResource => true;
- public bool IsXamlResource => false;
-
public BrowserIconResource(string uri = null)
{
+ Type = IconResourceType.Bitmap;
Uri = new Uri(uri ?? "pack://application:,,,/SafeExamBrowser.UserInterface.Desktop;component/Images/SafeExamBrowser.ico");
}
}
diff --git a/SafeExamBrowser.Browser/SafeExamBrowser.Browser.csproj b/SafeExamBrowser.Browser/SafeExamBrowser.Browser.csproj
index d9159f47..41ddff18 100644
--- a/SafeExamBrowser.Browser/SafeExamBrowser.Browser.csproj
+++ b/SafeExamBrowser.Browser/SafeExamBrowser.Browser.csproj
@@ -66,7 +66,6 @@
-
diff --git a/SafeExamBrowser.Client.Contracts/INotificationInfo.cs b/SafeExamBrowser.Client.Contracts/INotificationInfo.cs
index fe103dc7..c6e1de79 100644
--- a/SafeExamBrowser.Client.Contracts/INotificationInfo.cs
+++ b/SafeExamBrowser.Client.Contracts/INotificationInfo.cs
@@ -23,6 +23,6 @@ namespace SafeExamBrowser.Client.Contracts
///
/// The resource providing the notification icon.
///
- IIconResource IconResource { get; }
+ IconResource IconResource { get; }
}
}
diff --git a/SafeExamBrowser.Client.UnitTests/Operations/BrowserOperationTests.cs b/SafeExamBrowser.Client.UnitTests/Operations/BrowserOperationTests.cs
index acb181d8..b7b91f58 100644
--- a/SafeExamBrowser.Client.UnitTests/Operations/BrowserOperationTests.cs
+++ b/SafeExamBrowser.Client.UnitTests/Operations/BrowserOperationTests.cs
@@ -49,8 +49,8 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
sut.Perform();
browser.Verify(c => c.Initialize(), Times.Once);
- actionCenter.Verify(a => a.AddApplicationControl(It.IsAny()), Times.Once);
- taskbar.Verify(t => t.AddApplicationControl(It.IsAny()), Times.Once);
+ actionCenter.Verify(a => a.AddApplicationControl(It.IsAny(), true), Times.Once);
+ taskbar.Verify(t => t.AddApplicationControl(It.IsAny(), true), Times.Once);
}
[TestMethod]
diff --git a/SafeExamBrowser.Client/CompositionRoot.cs b/SafeExamBrowser.Client/CompositionRoot.cs
index ce209094..553b7940 100644
--- a/SafeExamBrowser.Client/CompositionRoot.cs
+++ b/SafeExamBrowser.Client/CompositionRoot.cs
@@ -198,7 +198,6 @@ namespace SafeExamBrowser.Client
{
var moduleLogger = ModuleLogger(nameof(BrowserApplication));
var browser = new BrowserApplication(context.AppConfig, context.Settings.Browser, messageBox, moduleLogger, text, uiFactory);
- var browserInfo = new BrowserApplicationInfo();
var operation = new BrowserOperation(actionCenter, context, logger, taskbar, uiFactory);
context.Browser = browser;
diff --git a/SafeExamBrowser.Client/Notifications/AboutNotificationIconResource.cs b/SafeExamBrowser.Client/Notifications/AboutNotificationIconResource.cs
deleted file mode 100644
index 57ecab7c..00000000
--- a/SafeExamBrowser.Client/Notifications/AboutNotificationIconResource.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET)
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- */
-
-using System;
-using SafeExamBrowser.Core.Contracts;
-
-namespace SafeExamBrowser.Client.Notifications
-{
- internal class AboutNotificationIconResource : IIconResource
- {
- public Uri Uri => new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Desktop;component/Images/AboutNotification.xaml");
- public bool IsBitmapResource => false;
- public bool IsXamlResource => true;
- }
-}
diff --git a/SafeExamBrowser.Client/Notifications/AboutNotificationInfo.cs b/SafeExamBrowser.Client/Notifications/AboutNotificationInfo.cs
index 8b13e3d6..5f50c675 100644
--- a/SafeExamBrowser.Client/Notifications/AboutNotificationInfo.cs
+++ b/SafeExamBrowser.Client/Notifications/AboutNotificationInfo.cs
@@ -6,6 +6,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
+using System;
using SafeExamBrowser.Client.Contracts;
using SafeExamBrowser.Core.Contracts;
using SafeExamBrowser.I18n.Contracts;
@@ -14,14 +15,17 @@ namespace SafeExamBrowser.Client.Notifications
{
internal class AboutNotificationInfo : INotificationInfo
{
- private IText text;
-
- public string Tooltip => text.Get(TextKey.Notification_AboutTooltip);
- public IIconResource IconResource { get; } = new AboutNotificationIconResource();
+ public string Tooltip { get; }
+ public IconResource IconResource { get; }
public AboutNotificationInfo(IText text)
{
- this.text = text;
+ IconResource = new IconResource
+ {
+ Type = IconResourceType.Xaml,
+ Uri = new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Desktop;component/Images/AboutNotification.xaml")
+ };
+ Tooltip = text.Get(TextKey.Notification_AboutTooltip);
}
}
}
diff --git a/SafeExamBrowser.Client/Notifications/LogNotificationIconResource.cs b/SafeExamBrowser.Client/Notifications/LogNotificationIconResource.cs
deleted file mode 100644
index 6078f8b1..00000000
--- a/SafeExamBrowser.Client/Notifications/LogNotificationIconResource.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET)
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- */
-
-using System;
-using SafeExamBrowser.Core.Contracts;
-
-namespace SafeExamBrowser.Client.Notifications
-{
- internal class LogNotificationIconResource : IIconResource
- {
- public Uri Uri => new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Desktop;component/Images/LogNotification.ico");
- public bool IsBitmapResource => true;
- public bool IsXamlResource => false;
- }
-}
diff --git a/SafeExamBrowser.Client/Notifications/LogNotificationInfo.cs b/SafeExamBrowser.Client/Notifications/LogNotificationInfo.cs
index 8f809860..dd7a9cd7 100644
--- a/SafeExamBrowser.Client/Notifications/LogNotificationInfo.cs
+++ b/SafeExamBrowser.Client/Notifications/LogNotificationInfo.cs
@@ -6,6 +6,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
+using System;
using SafeExamBrowser.Client.Contracts;
using SafeExamBrowser.Core.Contracts;
using SafeExamBrowser.I18n.Contracts;
@@ -14,13 +15,17 @@ namespace SafeExamBrowser.Client.Notifications
{
internal class LogNotificationInfo : INotificationInfo
{
- public string Tooltip { get; private set; }
- public IIconResource IconResource { get; private set; }
+ public string Tooltip { get; }
+ public IconResource IconResource { get; }
public LogNotificationInfo(IText text)
{
+ IconResource = new IconResource
+ {
+ Type = IconResourceType.Bitmap,
+ Uri = new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Desktop;component/Images/LogNotification.ico")
+ };
Tooltip = text.Get(TextKey.Notification_LogTooltip);
- IconResource = new LogNotificationIconResource();
}
}
}
diff --git a/SafeExamBrowser.Client/Operations/ApplicationOperation.cs b/SafeExamBrowser.Client/Operations/ApplicationOperation.cs
index 7087c04a..fccae8b3 100644
--- a/SafeExamBrowser.Client/Operations/ApplicationOperation.cs
+++ b/SafeExamBrowser.Client/Operations/ApplicationOperation.cs
@@ -117,7 +117,6 @@ namespace SafeExamBrowser.Client.Operations
if (result == FactoryResult.Success)
{
- application.Initialize();
Context.Applications.Add(application);
}
else
@@ -129,7 +128,10 @@ namespace SafeExamBrowser.Client.Operations
private void FinalizeApplications()
{
- // TODO: Terminate all running applications!
+ foreach (var application in Context.Applications)
+ {
+ application.Terminate();
+ }
}
private OperationResult HandleAutoTerminationFailure(IList applications)
diff --git a/SafeExamBrowser.Client/Operations/BrowserOperation.cs b/SafeExamBrowser.Client/Operations/BrowserOperation.cs
index 3a813074..e44cd11c 100644
--- a/SafeExamBrowser.Client/Operations/BrowserOperation.cs
+++ b/SafeExamBrowser.Client/Operations/BrowserOperation.cs
@@ -45,8 +45,15 @@ namespace SafeExamBrowser.Client.Operations
Context.Browser.Initialize();
- actionCenter.AddApplicationControl(uiFactory.CreateApplicationControl(Context.Browser, Location.ActionCenter));
- taskbar.AddApplicationControl(uiFactory.CreateApplicationControl(Context.Browser, Location.Taskbar));
+ if (Context.Settings.ActionCenter.EnableActionCenter)
+ {
+ actionCenter.AddApplicationControl(uiFactory.CreateApplicationControl(Context.Browser, Location.ActionCenter), true);
+ }
+
+ if (Context.Settings.Taskbar.EnableTaskbar)
+ {
+ taskbar.AddApplicationControl(uiFactory.CreateApplicationControl(Context.Browser, Location.Taskbar), true);
+ }
return OperationResult.Success;
}
diff --git a/SafeExamBrowser.Client/Operations/ShellOperation.cs b/SafeExamBrowser.Client/Operations/ShellOperation.cs
index f71f7ccf..31d0fdae 100644
--- a/SafeExamBrowser.Client/Operations/ShellOperation.cs
+++ b/SafeExamBrowser.Client/Operations/ShellOperation.cs
@@ -124,6 +124,7 @@ namespace SafeExamBrowser.Client.Operations
logger.Info("Initializing action center...");
actionCenter.InitializeText(text);
+ InitializeApplicationsFor(Location.ActionCenter);
InitializeAboutNotificationForActionCenter();
InitializeAudioForActionCenter();
InitializeClockForActionCenter();
@@ -145,6 +146,7 @@ namespace SafeExamBrowser.Client.Operations
logger.Info("Initializing taskbar...");
taskbar.InitializeText(text);
+ InitializeApplicationsFor(Location.Taskbar);
InitializeAboutNotificationForTaskbar();
InitializeLogNotificationForTaskbar();
InitializePowerSupplyForTaskbar();
@@ -159,6 +161,24 @@ namespace SafeExamBrowser.Client.Operations
}
}
+ private void InitializeApplicationsFor(Location location)
+ {
+ foreach (var application in Context.Applications)
+ {
+ var control = uiFactory.CreateApplicationControl(application, location);
+
+ switch (location)
+ {
+ case Location.ActionCenter:
+ actionCenter.AddApplicationControl(control);
+ break;
+ case Location.Taskbar:
+ taskbar.AddApplicationControl(control);
+ break;
+ }
+ }
+ }
+
private void InitializeSystemComponents()
{
audio.Initialize();
diff --git a/SafeExamBrowser.Client/SafeExamBrowser.Client.csproj b/SafeExamBrowser.Client/SafeExamBrowser.Client.csproj
index cd77d41e..5472797a 100644
--- a/SafeExamBrowser.Client/SafeExamBrowser.Client.csproj
+++ b/SafeExamBrowser.Client/SafeExamBrowser.Client.csproj
@@ -84,10 +84,8 @@
-
-
diff --git a/SafeExamBrowser.Configuration/ConfigurationData/DataMapper.Applications.cs b/SafeExamBrowser.Configuration/ConfigurationData/DataMapper.Applications.cs
index 25ea5fc3..4d241670 100644
--- a/SafeExamBrowser.Configuration/ConfigurationData/DataMapper.Applications.cs
+++ b/SafeExamBrowser.Configuration/ConfigurationData/DataMapper.Applications.cs
@@ -102,6 +102,11 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
application.AutoTerminate = autoTerminate;
}
+ if (applicationData.TryGetValue(Keys.Applications.Description, out v) && v is string description)
+ {
+ application.Description = description;
+ }
+
if (applicationData.TryGetValue(Keys.Applications.DisplayName, out v) && v is string displayName)
{
application.DisplayName = displayName;
diff --git a/SafeExamBrowser.Configuration/ConfigurationData/Keys.cs b/SafeExamBrowser.Configuration/ConfigurationData/Keys.cs
index 8a4643cb..f9d8634f 100644
--- a/SafeExamBrowser.Configuration/ConfigurationData/Keys.cs
+++ b/SafeExamBrowser.Configuration/ConfigurationData/Keys.cs
@@ -26,6 +26,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
internal const string AutoStart = "autostart";
internal const string AutoTerminate = "strongKill";
internal const string Blacklist = "prohibitedProcesses";
+ internal const string Description = "description";
internal const string DisplayName = "title";
internal const string ExecutableName = "executable";
internal const string ExecutablePath = "path";
diff --git a/SafeExamBrowser.Core.Contracts/IIconResource.cs b/SafeExamBrowser.Core.Contracts/IconResource.cs
similarity index 57%
rename from SafeExamBrowser.Core.Contracts/IIconResource.cs
rename to SafeExamBrowser.Core.Contracts/IconResource.cs
index fb192573..beff06d3 100644
--- a/SafeExamBrowser.Core.Contracts/IIconResource.cs
+++ b/SafeExamBrowser.Core.Contracts/IconResource.cs
@@ -13,21 +13,16 @@ namespace SafeExamBrowser.Core.Contracts
///
/// Defines an icon resource, i.e. the path to and type of an icon.
///
- public interface IIconResource
+ public class IconResource
{
///
- /// The pointing to the icon.
+ /// Defines the data type of the resource.
///
- Uri Uri { get; }
+ public IconResourceType Type { get; set; }
///
- /// Indicates whether the icon resource consists of a bitmap image (i.e. raster graphics).
+ /// The pointing to the icon data.
///
- bool IsBitmapResource { get; }
-
- ///
- /// Indicates whether the icon resource consists of XAML markup (i.e. vector graphics).
- ///
- bool IsXamlResource { get; }
+ public Uri Uri { get; set; }
}
}
diff --git a/SafeExamBrowser.Core.Contracts/IconResourceType.cs b/SafeExamBrowser.Core.Contracts/IconResourceType.cs
new file mode 100644
index 00000000..11a004aa
--- /dev/null
+++ b/SafeExamBrowser.Core.Contracts/IconResourceType.cs
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET)
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+namespace SafeExamBrowser.Core.Contracts
+{
+ ///
+ /// Defines the data format of an icon resource.
+ ///
+ public enum IconResourceType
+ {
+ ///
+ /// The icon resource is a bitmap image (i.e. raster graphics).
+ ///
+ Bitmap,
+
+ ///
+ /// The icon resource is a file with embedded icon data (e.g. an executable).
+ ///
+ Embedded,
+
+ ///
+ /// The icon resource consists of XAML markup (i.e. vector graphics).
+ ///
+ Xaml
+ }
+}
diff --git a/SafeExamBrowser.Core.Contracts/SafeExamBrowser.Core.Contracts.csproj b/SafeExamBrowser.Core.Contracts/SafeExamBrowser.Core.Contracts.csproj
index ff9f5b20..2dad9aba 100644
--- a/SafeExamBrowser.Core.Contracts/SafeExamBrowser.Core.Contracts.csproj
+++ b/SafeExamBrowser.Core.Contracts/SafeExamBrowser.Core.Contracts.csproj
@@ -53,7 +53,8 @@
-
+
+
diff --git a/SafeExamBrowser.I18n.Contracts/TextKey.cs b/SafeExamBrowser.I18n.Contracts/TextKey.cs
index 9188234c..3872fd9a 100644
--- a/SafeExamBrowser.I18n.Contracts/TextKey.cs
+++ b/SafeExamBrowser.I18n.Contracts/TextKey.cs
@@ -18,6 +18,7 @@ namespace SafeExamBrowser.I18n.Contracts
Browser_BlockedPageButton,
Browser_BlockedPageMessage,
Browser_BlockedPageTitle,
+ Browser_Tooltip,
BrowserWindow_DeveloperConsoleMenuItem,
BrowserWindow_ZoomMenuItem,
Build,
diff --git a/SafeExamBrowser.I18n/Text.xml b/SafeExamBrowser.I18n/Text.xml
index 3fa7dc06..56ee7796 100644
--- a/SafeExamBrowser.I18n/Text.xml
+++ b/SafeExamBrowser.I18n/Text.xml
@@ -12,6 +12,9 @@
Page Blocked
+
+ Browser Application
+
Developer Console
diff --git a/SafeExamBrowser.Settings/Applications/WhitelistApplication.cs b/SafeExamBrowser.Settings/Applications/WhitelistApplication.cs
index 4d162450..e7515be5 100644
--- a/SafeExamBrowser.Settings/Applications/WhitelistApplication.cs
+++ b/SafeExamBrowser.Settings/Applications/WhitelistApplication.cs
@@ -42,6 +42,11 @@ namespace SafeExamBrowser.Settings.Applications
///
public bool AutoTerminate { get; set; }
+ ///
+ /// Provides further information about the application.
+ ///
+ public string Description { get; set; }
+
///
/// The display name to be used for the application (e.g. in the shell).
///
diff --git a/SafeExamBrowser.UserInterface.Contracts/Browser/IBrowserWindow.cs b/SafeExamBrowser.UserInterface.Contracts/Browser/IBrowserWindow.cs
index c27faab5..dda2c424 100644
--- a/SafeExamBrowser.UserInterface.Contracts/Browser/IBrowserWindow.cs
+++ b/SafeExamBrowser.UserInterface.Contracts/Browser/IBrowserWindow.cs
@@ -75,7 +75,7 @@ namespace SafeExamBrowser.UserInterface.Contracts.Browser
///
/// Updates the icon of the browser window.
///
- void UpdateIcon(IIconResource icon);
+ void UpdateIcon(IconResource icon);
///
/// Updates the loading state according to the given value.
diff --git a/SafeExamBrowser.UserInterface.Contracts/Shell/IActionCenter.cs b/SafeExamBrowser.UserInterface.Contracts/Shell/IActionCenter.cs
index 588036c8..7a74b4a5 100644
--- a/SafeExamBrowser.UserInterface.Contracts/Shell/IActionCenter.cs
+++ b/SafeExamBrowser.UserInterface.Contracts/Shell/IActionCenter.cs
@@ -29,7 +29,7 @@ namespace SafeExamBrowser.UserInterface.Contracts.Shell
///
/// Adds the given application control to the action center.
///
- void AddApplicationControl(IApplicationControl control);
+ void AddApplicationControl(IApplicationControl control, bool atFirstPosition = false);
///
/// Adds the given notification control to the action center.
diff --git a/SafeExamBrowser.UserInterface.Contracts/Shell/ITaskbar.cs b/SafeExamBrowser.UserInterface.Contracts/Shell/ITaskbar.cs
index b5f9885d..e1989121 100644
--- a/SafeExamBrowser.UserInterface.Contracts/Shell/ITaskbar.cs
+++ b/SafeExamBrowser.UserInterface.Contracts/Shell/ITaskbar.cs
@@ -29,7 +29,7 @@ namespace SafeExamBrowser.UserInterface.Contracts.Shell
///
/// Adds the given application control to the taskbar.
///
- void AddApplicationControl(IApplicationControl control);
+ void AddApplicationControl(IApplicationControl control, bool atFirstPosition = false);
///
/// Adds the given notification control to the taskbar.
diff --git a/SafeExamBrowser.UserInterface.Desktop/ActionCenter.xaml.cs b/SafeExamBrowser.UserInterface.Desktop/ActionCenter.xaml.cs
index eaf0b0a4..2b81aa90 100644
--- a/SafeExamBrowser.UserInterface.Desktop/ActionCenter.xaml.cs
+++ b/SafeExamBrowser.UserInterface.Desktop/ActionCenter.xaml.cs
@@ -30,11 +30,18 @@ namespace SafeExamBrowser.UserInterface.Desktop
InitializeActionCenter();
}
- public void AddApplicationControl(IApplicationControl control)
+ public void AddApplicationControl(IApplicationControl control, bool atFirstPosition = false)
{
if (control is UIElement uiElement)
{
- ApplicationPanel.Children.Add(uiElement);
+ if (atFirstPosition)
+ {
+ ApplicationPanel.Children.Insert(0, uiElement);
+ }
+ else
+ {
+ ApplicationPanel.Children.Add(uiElement);
+ }
}
}
diff --git a/SafeExamBrowser.UserInterface.Desktop/BrowserWindow.xaml.cs b/SafeExamBrowser.UserInterface.Desktop/BrowserWindow.xaml.cs
index b1fd3b1a..59f83a39 100644
--- a/SafeExamBrowser.UserInterface.Desktop/BrowserWindow.xaml.cs
+++ b/SafeExamBrowser.UserInterface.Desktop/BrowserWindow.xaml.cs
@@ -104,7 +104,7 @@ namespace SafeExamBrowser.UserInterface.Desktop
Dispatcher.Invoke(() => UrlTextBox.Text = url);
}
- public void UpdateIcon(IIconResource icon)
+ public void UpdateIcon(IconResource icon)
{
Dispatcher.InvokeAsync(() => Icon = new BitmapImage(icon.Uri));
}
@@ -282,10 +282,10 @@ namespace SafeExamBrowser.UserInterface.Desktop
var forwardUri = new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Desktop;component/Images/NavigateForward.xaml");
var menuUri = new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Desktop;component/Images/Menu.xaml");
var reloadUri = new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Desktop;component/Images/Reload.xaml");
- var backward = new XamlIconResource(backUri);
- var forward = new XamlIconResource(forwardUri);
- var menu = new XamlIconResource(menuUri);
- var reload = new XamlIconResource(reloadUri);
+ var backward = new IconResource { Type = IconResourceType.Xaml, Uri = backUri };
+ var forward = new IconResource { Type = IconResourceType.Xaml, Uri = forwardUri };
+ var menu = new IconResource { Type = IconResourceType.Xaml, Uri = menuUri };
+ var reload = new IconResource { Type = IconResourceType.Xaml, Uri = reloadUri };
BackwardButton.Content = IconResourceLoader.Load(backward);
ForwardButton.Content = IconResourceLoader.Load(forward);
diff --git a/SafeExamBrowser.UserInterface.Desktop/Controls/ActionCenterApplicationButton.xaml.cs b/SafeExamBrowser.UserInterface.Desktop/Controls/ActionCenterApplicationButton.xaml.cs
index 8fa47cec..0e58921f 100644
--- a/SafeExamBrowser.UserInterface.Desktop/Controls/ActionCenterApplicationButton.xaml.cs
+++ b/SafeExamBrowser.UserInterface.Desktop/Controls/ActionCenterApplicationButton.xaml.cs
@@ -16,12 +16,12 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls
{
public partial class ActionCenterApplicationButton : UserControl
{
- private IApplicationInfo info;
+ private ApplicationInfo info;
private IApplicationInstance instance;
internal event EventHandler Clicked;
- public ActionCenterApplicationButton(IApplicationInfo info, IApplicationInstance instance = null)
+ public ActionCenterApplicationButton(ApplicationInfo info, IApplicationInstance instance = null)
{
this.info = info;
this.instance = instance;
@@ -44,7 +44,7 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls
}
}
- private void Instance_IconChanged(IIconResource icon)
+ private void Instance_IconChanged(IconResource icon)
{
Dispatcher.InvokeAsync(() => Icon.Content = IconResourceLoader.Load(icon));
}
diff --git a/SafeExamBrowser.UserInterface.Desktop/Controls/ActionCenterAudioControl.xaml.cs b/SafeExamBrowser.UserInterface.Desktop/Controls/ActionCenterAudioControl.xaml.cs
index 0f9b688d..d7ee7830 100644
--- a/SafeExamBrowser.UserInterface.Desktop/Controls/ActionCenterAudioControl.xaml.cs
+++ b/SafeExamBrowser.UserInterface.Desktop/Controls/ActionCenterAudioControl.xaml.cs
@@ -13,6 +13,7 @@ using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Media;
using System.Windows.Threading;
+using SafeExamBrowser.Core.Contracts;
using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.SystemComponents.Contracts.Audio;
using SafeExamBrowser.UserInterface.Contracts.Shell;
@@ -25,8 +26,8 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls
private readonly IAudio audio;
private readonly IText text;
private bool muted;
- private XamlIconResource MutedIcon;
- private XamlIconResource NoDeviceIcon;
+ private IconResource MutedIcon;
+ private IconResource NoDeviceIcon;
public ActionCenterAudioControl(IAudio audio, IText text)
{
@@ -50,8 +51,8 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls
Button.Click += (o, args) => Popup.IsOpen = !Popup.IsOpen;
Button.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => Popup.IsOpen = Popup.IsMouseOver));
MuteButton.Click += MuteButton_Click;
- MutedIcon = new XamlIconResource(new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Desktop;component/Images/Audio_Muted.xaml"));
- NoDeviceIcon = new XamlIconResource(new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Desktop;component/Images/Audio_Light_NoDevice.xaml"));
+ MutedIcon = new IconResource { Type = IconResourceType.Xaml, Uri = new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Desktop;component/Images/Audio_Muted.xaml") };
+ NoDeviceIcon = new IconResource { Type = IconResourceType.Xaml, Uri = new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Desktop;component/Images/Audio_Light_NoDevice.xaml") };
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;
@@ -146,7 +147,7 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls
{
var icon = volume > 0.66 ? "100" : (volume > 0.33 ? "66" : "33");
var uri = new Uri($"pack://application:,,,/SafeExamBrowser.UserInterface.Desktop;component/Images/Audio_Light_{icon}.xaml");
- var resource = new XamlIconResource(uri);
+ var resource = new IconResource { Type = IconResourceType.Xaml, Uri = uri};
return IconResourceLoader.Load(resource);
}
diff --git a/SafeExamBrowser.UserInterface.Desktop/Controls/ActionCenterQuitButton.xaml.cs b/SafeExamBrowser.UserInterface.Desktop/Controls/ActionCenterQuitButton.xaml.cs
index 1f7cd5bf..3c43d031 100644
--- a/SafeExamBrowser.UserInterface.Desktop/Controls/ActionCenterQuitButton.xaml.cs
+++ b/SafeExamBrowser.UserInterface.Desktop/Controls/ActionCenterQuitButton.xaml.cs
@@ -9,6 +9,7 @@
using System;
using System.ComponentModel;
using System.Windows.Controls;
+using SafeExamBrowser.Core.Contracts;
using SafeExamBrowser.UserInterface.Contracts.Shell.Events;
using SafeExamBrowser.UserInterface.Shared.Utilities;
@@ -27,7 +28,7 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls
private void InitializeControl()
{
var uri = new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Desktop;component/Images/ShutDown.xaml");
- var resource = new XamlIconResource(uri);
+ var resource = new IconResource { Type = IconResourceType.Xaml, Uri = uri };
Icon.Content = IconResourceLoader.Load(resource);
Button.Click += (o, args) => Clicked?.Invoke(new CancelEventArgs());
diff --git a/SafeExamBrowser.UserInterface.Desktop/Controls/ActionCenterWirelessNetworkControl.xaml.cs b/SafeExamBrowser.UserInterface.Desktop/Controls/ActionCenterWirelessNetworkControl.xaml.cs
index 6ec45f5d..60251f58 100644
--- a/SafeExamBrowser.UserInterface.Desktop/Controls/ActionCenterWirelessNetworkControl.xaml.cs
+++ b/SafeExamBrowser.UserInterface.Desktop/Controls/ActionCenterWirelessNetworkControl.xaml.cs
@@ -12,6 +12,7 @@ using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using FontAwesome.WPF;
+using SafeExamBrowser.Core.Contracts;
using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.SystemComponents.Contracts.WirelessNetwork;
using SafeExamBrowser.UserInterface.Contracts.Shell;
@@ -133,7 +134,7 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls
{
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);
+ var resource = new IconResource { Type = IconResourceType.Xaml, Uri = uri };
return IconResourceLoader.Load(resource);
}
diff --git a/SafeExamBrowser.UserInterface.Desktop/Controls/TaskbarApplicationInstanceButton.xaml.cs b/SafeExamBrowser.UserInterface.Desktop/Controls/TaskbarApplicationInstanceButton.xaml.cs
index b8d59716..a848b3c2 100644
--- a/SafeExamBrowser.UserInterface.Desktop/Controls/TaskbarApplicationInstanceButton.xaml.cs
+++ b/SafeExamBrowser.UserInterface.Desktop/Controls/TaskbarApplicationInstanceButton.xaml.cs
@@ -16,10 +16,10 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls
{
public partial class TaskbarApplicationInstanceButton : UserControl
{
- private IApplicationInfo info;
+ private ApplicationInfo info;
private IApplicationInstance instance;
- public TaskbarApplicationInstanceButton(IApplicationInstance instance, IApplicationInfo info)
+ public TaskbarApplicationInstanceButton(IApplicationInstance instance, ApplicationInfo info)
{
this.info = info;
this.instance = instance;
@@ -43,7 +43,7 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls
instance.Activate();
}
- private void Instance_IconChanged(IIconResource icon)
+ private void Instance_IconChanged(IconResource icon)
{
Dispatcher.InvokeAsync(() => Icon.Content = IconResourceLoader.Load(icon));
}
diff --git a/SafeExamBrowser.UserInterface.Desktop/Controls/TaskbarAudioControl.xaml.cs b/SafeExamBrowser.UserInterface.Desktop/Controls/TaskbarAudioControl.xaml.cs
index 321d6e7d..3435f8d5 100644
--- a/SafeExamBrowser.UserInterface.Desktop/Controls/TaskbarAudioControl.xaml.cs
+++ b/SafeExamBrowser.UserInterface.Desktop/Controls/TaskbarAudioControl.xaml.cs
@@ -12,6 +12,7 @@ using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Media;
+using SafeExamBrowser.Core.Contracts;
using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.SystemComponents.Contracts.Audio;
using SafeExamBrowser.UserInterface.Contracts.Shell;
@@ -24,8 +25,8 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls
private readonly IAudio audio;
private readonly IText text;
private bool muted;
- private XamlIconResource MutedIcon;
- private XamlIconResource NoDeviceIcon;
+ private IconResource MutedIcon;
+ private IconResource NoDeviceIcon;
public TaskbarAudioControl(IAudio audio, IText text)
{
@@ -49,8 +50,8 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls
Button.Click += (o, args) => Popup.IsOpen = !Popup.IsOpen;
Button.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => Popup.IsOpen = Popup.IsMouseOver));
MuteButton.Click += MuteButton_Click;
- MutedIcon = new XamlIconResource(new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Desktop;component/Images/Audio_Muted.xaml"));
- NoDeviceIcon = new XamlIconResource(new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Desktop;component/Images/Audio_NoDevice.xaml"));
+ MutedIcon = new IconResource { Type = IconResourceType.Xaml, Uri = new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Desktop;component/Images/Audio_Muted.xaml") };
+ NoDeviceIcon = new IconResource { Type = IconResourceType.Xaml, Uri = new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Desktop;component/Images/Audio_NoDevice.xaml") };
Popup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => Popup.IsOpen = IsMouseOver));
Volume.ValueChanged += Volume_ValueChanged;
@@ -153,7 +154,7 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls
{
var icon = volume > 0.66 ? "100" : (volume > 0.33 ? "66" : "33");
var uri = new Uri($"pack://application:,,,/SafeExamBrowser.UserInterface.Desktop;component/Images/Audio_{icon}.xaml");
- var resource = new XamlIconResource(uri);
+ var resource = new IconResource { Type = IconResourceType.Xaml, Uri = uri };
return IconResourceLoader.Load(resource);
}
diff --git a/SafeExamBrowser.UserInterface.Desktop/Controls/TaskbarQuitButton.xaml.cs b/SafeExamBrowser.UserInterface.Desktop/Controls/TaskbarQuitButton.xaml.cs
index 4de0e8be..1067e0c1 100644
--- a/SafeExamBrowser.UserInterface.Desktop/Controls/TaskbarQuitButton.xaml.cs
+++ b/SafeExamBrowser.UserInterface.Desktop/Controls/TaskbarQuitButton.xaml.cs
@@ -10,6 +10,7 @@ using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
+using SafeExamBrowser.Core.Contracts;
using SafeExamBrowser.UserInterface.Contracts.Shell.Events;
using SafeExamBrowser.UserInterface.Shared.Utilities;
@@ -33,7 +34,7 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls
private void LoadIcon()
{
var uri = new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Desktop;component/Images/ShutDown.xaml");
- var resource = new XamlIconResource(uri);
+ var resource = new IconResource { Type = IconResourceType.Xaml, Uri = uri };
Button.Content = IconResourceLoader.Load(resource);
}
diff --git a/SafeExamBrowser.UserInterface.Desktop/Controls/TaskbarWirelessNetworkControl.xaml.cs b/SafeExamBrowser.UserInterface.Desktop/Controls/TaskbarWirelessNetworkControl.xaml.cs
index bec84b6d..b32549af 100644
--- a/SafeExamBrowser.UserInterface.Desktop/Controls/TaskbarWirelessNetworkControl.xaml.cs
+++ b/SafeExamBrowser.UserInterface.Desktop/Controls/TaskbarWirelessNetworkControl.xaml.cs
@@ -12,6 +12,7 @@ using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using FontAwesome.WPF;
+using SafeExamBrowser.Core.Contracts;
using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.SystemComponents.Contracts.WirelessNetwork;
using SafeExamBrowser.UserInterface.Contracts.Shell;
@@ -142,7 +143,7 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls
{
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");
- var resource = new XamlIconResource(uri);
+ var resource = new IconResource { Type = IconResourceType.Xaml, Uri = uri };
return IconResourceLoader.Load(resource);
}
diff --git a/SafeExamBrowser.UserInterface.Desktop/Taskbar.xaml.cs b/SafeExamBrowser.UserInterface.Desktop/Taskbar.xaml.cs
index eb69aacd..d9937372 100644
--- a/SafeExamBrowser.UserInterface.Desktop/Taskbar.xaml.cs
+++ b/SafeExamBrowser.UserInterface.Desktop/Taskbar.xaml.cs
@@ -36,11 +36,18 @@ namespace SafeExamBrowser.UserInterface.Desktop
InitializeTaskbar();
}
- public void AddApplicationControl(IApplicationControl control)
+ public void AddApplicationControl(IApplicationControl control, bool atFirstPosition = false)
{
if (control is UIElement uiElement)
{
- ApplicationStackPanel.Children.Add(uiElement);
+ if (atFirstPosition)
+ {
+ ApplicationStackPanel.Children.Insert(0, uiElement);
+ }
+ else
+ {
+ ApplicationStackPanel.Children.Add(uiElement);
+ }
}
}
diff --git a/SafeExamBrowser.UserInterface.Mobile/ActionCenter.xaml.cs b/SafeExamBrowser.UserInterface.Mobile/ActionCenter.xaml.cs
index a5d87517..8b423f06 100644
--- a/SafeExamBrowser.UserInterface.Mobile/ActionCenter.xaml.cs
+++ b/SafeExamBrowser.UserInterface.Mobile/ActionCenter.xaml.cs
@@ -30,11 +30,18 @@ namespace SafeExamBrowser.UserInterface.Mobile
InitializeActionCenter();
}
- public void AddApplicationControl(IApplicationControl control)
+ public void AddApplicationControl(IApplicationControl control, bool atFirstPosition = false)
{
if (control is UIElement uiElement)
{
- ApplicationPanel.Children.Add(uiElement);
+ if (atFirstPosition)
+ {
+ ApplicationPanel.Children.Insert(0, uiElement);
+ }
+ else
+ {
+ ApplicationPanel.Children.Add(uiElement);
+ }
}
}
diff --git a/SafeExamBrowser.UserInterface.Mobile/BrowserWindow.xaml.cs b/SafeExamBrowser.UserInterface.Mobile/BrowserWindow.xaml.cs
index c61a6852..3283080c 100644
--- a/SafeExamBrowser.UserInterface.Mobile/BrowserWindow.xaml.cs
+++ b/SafeExamBrowser.UserInterface.Mobile/BrowserWindow.xaml.cs
@@ -104,7 +104,7 @@ namespace SafeExamBrowser.UserInterface.Mobile
Dispatcher.Invoke(() => UrlTextBox.Text = url);
}
- public void UpdateIcon(IIconResource icon)
+ public void UpdateIcon(IconResource icon)
{
Dispatcher.InvokeAsync(() => Icon = new BitmapImage(icon.Uri));
}
@@ -291,10 +291,10 @@ namespace SafeExamBrowser.UserInterface.Mobile
var forwardUri = new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Desktop;component/Images/NavigateForward.xaml");
var menuUri = new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Desktop;component/Images/Menu.xaml");
var reloadUri = new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Desktop;component/Images/Reload.xaml");
- var backward = new XamlIconResource(backUri);
- var forward = new XamlIconResource(forwardUri);
- var menu = new XamlIconResource(menuUri);
- var reload = new XamlIconResource(reloadUri);
+ var backward = new IconResource { Type = IconResourceType.Xaml, Uri = backUri };
+ var forward = new IconResource { Type = IconResourceType.Xaml, Uri = forwardUri };
+ var menu = new IconResource { Type = IconResourceType.Xaml, Uri = menuUri };
+ var reload = new IconResource { Type = IconResourceType.Xaml, Uri = reloadUri };
BackwardButton.Content = IconResourceLoader.Load(backward);
ForwardButton.Content = IconResourceLoader.Load(forward);
diff --git a/SafeExamBrowser.UserInterface.Mobile/Controls/ActionCenterApplicationButton.xaml.cs b/SafeExamBrowser.UserInterface.Mobile/Controls/ActionCenterApplicationButton.xaml.cs
index 2bb70ffd..eb31f74a 100644
--- a/SafeExamBrowser.UserInterface.Mobile/Controls/ActionCenterApplicationButton.xaml.cs
+++ b/SafeExamBrowser.UserInterface.Mobile/Controls/ActionCenterApplicationButton.xaml.cs
@@ -16,12 +16,12 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls
{
public partial class ActionCenterApplicationButton : UserControl
{
- private IApplicationInfo info;
+ private ApplicationInfo info;
private IApplicationInstance instance;
internal event EventHandler Clicked;
- public ActionCenterApplicationButton(IApplicationInfo info, IApplicationInstance instance = null)
+ public ActionCenterApplicationButton(ApplicationInfo info, IApplicationInstance instance = null)
{
this.info = info;
this.instance = instance;
@@ -44,7 +44,7 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls
}
}
- private void Instance_IconChanged(IIconResource icon)
+ private void Instance_IconChanged(IconResource icon)
{
Dispatcher.InvokeAsync(() => Icon.Content = IconResourceLoader.Load(icon));
}
diff --git a/SafeExamBrowser.UserInterface.Mobile/Controls/ActionCenterAudioControl.xaml.cs b/SafeExamBrowser.UserInterface.Mobile/Controls/ActionCenterAudioControl.xaml.cs
index 992128fb..385babb1 100644
--- a/SafeExamBrowser.UserInterface.Mobile/Controls/ActionCenterAudioControl.xaml.cs
+++ b/SafeExamBrowser.UserInterface.Mobile/Controls/ActionCenterAudioControl.xaml.cs
@@ -13,6 +13,7 @@ using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Media;
using System.Windows.Threading;
+using SafeExamBrowser.Core.Contracts;
using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.SystemComponents.Contracts.Audio;
using SafeExamBrowser.UserInterface.Contracts.Shell;
@@ -25,8 +26,8 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls
private readonly IAudio audio;
private readonly IText text;
private bool muted;
- private XamlIconResource MutedIcon;
- private XamlIconResource NoDeviceIcon;
+ private IconResource MutedIcon;
+ private IconResource NoDeviceIcon;
public ActionCenterAudioControl(IAudio audio, IText text)
{
@@ -50,8 +51,8 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls
Button.Click += (o, args) => Popup.IsOpen = !Popup.IsOpen;
Button.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => Popup.IsOpen = Popup.IsMouseOver));
MuteButton.Click += MuteButton_Click;
- MutedIcon = new XamlIconResource(new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Mobile;component/Images/Audio_Muted.xaml"));
- NoDeviceIcon = new XamlIconResource(new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Mobile;component/Images/Audio_Light_NoDevice.xaml"));
+ MutedIcon = new IconResource { Type = IconResourceType.Xaml, Uri = new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Mobile;component/Images/Audio_Muted.xaml") };
+ NoDeviceIcon = new IconResource { Type = IconResourceType.Xaml, Uri = new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Mobile;component/Images/Audio_Light_NoDevice.xaml") };
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;
@@ -145,7 +146,7 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls
{
var icon = volume > 0.66 ? "100" : (volume > 0.33 ? "66" : "33");
var uri = new Uri($"pack://application:,,,/SafeExamBrowser.UserInterface.Mobile;component/Images/Audio_Light_{icon}.xaml");
- var resource = new XamlIconResource(uri);
+ var resource = new IconResource { Type = IconResourceType.Xaml, Uri = uri };
return IconResourceLoader.Load(resource);
}
diff --git a/SafeExamBrowser.UserInterface.Mobile/Controls/ActionCenterQuitButton.xaml.cs b/SafeExamBrowser.UserInterface.Mobile/Controls/ActionCenterQuitButton.xaml.cs
index 18ab9a51..faa45d05 100644
--- a/SafeExamBrowser.UserInterface.Mobile/Controls/ActionCenterQuitButton.xaml.cs
+++ b/SafeExamBrowser.UserInterface.Mobile/Controls/ActionCenterQuitButton.xaml.cs
@@ -9,6 +9,7 @@
using System;
using System.ComponentModel;
using System.Windows.Controls;
+using SafeExamBrowser.Core.Contracts;
using SafeExamBrowser.UserInterface.Contracts.Shell.Events;
using SafeExamBrowser.UserInterface.Shared.Utilities;
@@ -27,7 +28,7 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls
private void InitializeControl()
{
var uri = new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Desktop;component/Images/ShutDown.xaml");
- var resource = new XamlIconResource(uri);
+ var resource = new IconResource { Type = IconResourceType.Xaml, Uri = uri };
Icon.Content = IconResourceLoader.Load(resource);
Button.Click += (o, args) => Clicked?.Invoke(new CancelEventArgs());
diff --git a/SafeExamBrowser.UserInterface.Mobile/Controls/ActionCenterWirelessNetworkControl.xaml.cs b/SafeExamBrowser.UserInterface.Mobile/Controls/ActionCenterWirelessNetworkControl.xaml.cs
index 3248fef0..99f2b65c 100644
--- a/SafeExamBrowser.UserInterface.Mobile/Controls/ActionCenterWirelessNetworkControl.xaml.cs
+++ b/SafeExamBrowser.UserInterface.Mobile/Controls/ActionCenterWirelessNetworkControl.xaml.cs
@@ -12,6 +12,7 @@ using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using FontAwesome.WPF;
+using SafeExamBrowser.Core.Contracts;
using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.SystemComponents.Contracts.WirelessNetwork;
using SafeExamBrowser.UserInterface.Contracts.Shell;
@@ -133,7 +134,7 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls
{
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);
+ var resource = new IconResource { Type = IconResourceType.Xaml, Uri = uri };
return IconResourceLoader.Load(resource);
}
diff --git a/SafeExamBrowser.UserInterface.Mobile/Controls/TaskbarApplicationInstanceButton.xaml.cs b/SafeExamBrowser.UserInterface.Mobile/Controls/TaskbarApplicationInstanceButton.xaml.cs
index c93cf56d..e901f343 100644
--- a/SafeExamBrowser.UserInterface.Mobile/Controls/TaskbarApplicationInstanceButton.xaml.cs
+++ b/SafeExamBrowser.UserInterface.Mobile/Controls/TaskbarApplicationInstanceButton.xaml.cs
@@ -16,10 +16,10 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls
{
public partial class TaskbarApplicationInstanceButton : UserControl
{
- private IApplicationInfo info;
+ private ApplicationInfo info;
private IApplicationInstance instance;
- public TaskbarApplicationInstanceButton(IApplicationInstance instance, IApplicationInfo info)
+ public TaskbarApplicationInstanceButton(IApplicationInstance instance, ApplicationInfo info)
{
this.info = info;
this.instance = instance;
@@ -43,7 +43,7 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls
instance.Activate();
}
- private void Instance_IconChanged(IIconResource icon)
+ private void Instance_IconChanged(IconResource icon)
{
Dispatcher.InvokeAsync(() => Icon.Content = IconResourceLoader.Load(icon));
}
diff --git a/SafeExamBrowser.UserInterface.Mobile/Controls/TaskbarAudioControl.xaml.cs b/SafeExamBrowser.UserInterface.Mobile/Controls/TaskbarAudioControl.xaml.cs
index d6458f55..6907412c 100644
--- a/SafeExamBrowser.UserInterface.Mobile/Controls/TaskbarAudioControl.xaml.cs
+++ b/SafeExamBrowser.UserInterface.Mobile/Controls/TaskbarAudioControl.xaml.cs
@@ -12,6 +12,7 @@ using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Media;
+using SafeExamBrowser.Core.Contracts;
using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.SystemComponents.Contracts.Audio;
using SafeExamBrowser.UserInterface.Contracts.Shell;
@@ -24,8 +25,8 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls
private readonly IAudio audio;
private readonly IText text;
private bool muted;
- private XamlIconResource MutedIcon;
- private XamlIconResource NoDeviceIcon;
+ private IconResource MutedIcon;
+ private IconResource NoDeviceIcon;
public TaskbarAudioControl(IAudio audio, IText text)
{
@@ -49,8 +50,8 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls
Button.Click += (o, args) => Popup.IsOpen = !Popup.IsOpen;
Button.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => Popup.IsOpen = Popup.IsMouseOver));
MuteButton.Click += MuteButton_Click;
- MutedIcon = new XamlIconResource(new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Mobile;component/Images/Audio_Muted.xaml"));
- NoDeviceIcon = new XamlIconResource(new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Mobile;component/Images/Audio_NoDevice.xaml"));
+ MutedIcon = new IconResource { Type = IconResourceType.Xaml, Uri = new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Mobile;component/Images/Audio_Muted.xaml") };
+ NoDeviceIcon = new IconResource { Type = IconResourceType.Xaml, Uri = new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Mobile;component/Images/Audio_NoDevice.xaml") };
Popup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => Popup.IsOpen = IsMouseOver));
Volume.ValueChanged += Volume_ValueChanged;
@@ -153,7 +154,7 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls
{
var icon = volume > 0.66 ? "100" : (volume > 0.33 ? "66" : "33");
var uri = new Uri($"pack://application:,,,/SafeExamBrowser.UserInterface.Mobile;component/Images/Audio_{icon}.xaml");
- var resource = new XamlIconResource(uri);
+ var resource = new IconResource { Type = IconResourceType.Xaml, Uri = uri };
return IconResourceLoader.Load(resource);
}
diff --git a/SafeExamBrowser.UserInterface.Mobile/Controls/TaskbarQuitButton.xaml.cs b/SafeExamBrowser.UserInterface.Mobile/Controls/TaskbarQuitButton.xaml.cs
index f351cbd4..ddf99ff0 100644
--- a/SafeExamBrowser.UserInterface.Mobile/Controls/TaskbarQuitButton.xaml.cs
+++ b/SafeExamBrowser.UserInterface.Mobile/Controls/TaskbarQuitButton.xaml.cs
@@ -10,6 +10,7 @@ using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
+using SafeExamBrowser.Core.Contracts;
using SafeExamBrowser.UserInterface.Contracts.Shell.Events;
using SafeExamBrowser.UserInterface.Shared.Utilities;
@@ -33,7 +34,7 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls
private void LoadIcon()
{
var uri = new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Desktop;component/Images/ShutDown.xaml");
- var resource = new XamlIconResource(uri);
+ var resource = new IconResource { Type = IconResourceType.Xaml, Uri = uri };
Button.Content = IconResourceLoader.Load(resource);
}
diff --git a/SafeExamBrowser.UserInterface.Mobile/Controls/TaskbarWirelessNetworkControl.xaml.cs b/SafeExamBrowser.UserInterface.Mobile/Controls/TaskbarWirelessNetworkControl.xaml.cs
index 80d05004..8d6267e9 100644
--- a/SafeExamBrowser.UserInterface.Mobile/Controls/TaskbarWirelessNetworkControl.xaml.cs
+++ b/SafeExamBrowser.UserInterface.Mobile/Controls/TaskbarWirelessNetworkControl.xaml.cs
@@ -12,6 +12,7 @@ using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using FontAwesome.WPF;
+using SafeExamBrowser.Core.Contracts;
using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.SystemComponents.Contracts.WirelessNetwork;
using SafeExamBrowser.UserInterface.Contracts.Shell;
@@ -142,7 +143,7 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls
{
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");
- var resource = new XamlIconResource(uri);
+ var resource = new IconResource { Type = IconResourceType.Xaml, Uri = uri };
return IconResourceLoader.Load(resource);
}
diff --git a/SafeExamBrowser.UserInterface.Mobile/Taskbar.xaml.cs b/SafeExamBrowser.UserInterface.Mobile/Taskbar.xaml.cs
index cbe19867..23f61077 100644
--- a/SafeExamBrowser.UserInterface.Mobile/Taskbar.xaml.cs
+++ b/SafeExamBrowser.UserInterface.Mobile/Taskbar.xaml.cs
@@ -36,11 +36,18 @@ namespace SafeExamBrowser.UserInterface.Mobile
InitializeTaskbar();
}
- public void AddApplicationControl(IApplicationControl control)
+ public void AddApplicationControl(IApplicationControl control, bool atFirstPosition = false)
{
if (control is UIElement uiElement)
{
- ApplicationStackPanel.Children.Add(uiElement);
+ if (atFirstPosition)
+ {
+ ApplicationStackPanel.Children.Insert(0, uiElement);
+ }
+ else
+ {
+ ApplicationStackPanel.Children.Add(uiElement);
+ }
}
}
diff --git a/SafeExamBrowser.UserInterface.Shared/SafeExamBrowser.UserInterface.Shared.csproj b/SafeExamBrowser.UserInterface.Shared/SafeExamBrowser.UserInterface.Shared.csproj
index 9793bd4e..5f2c7568 100644
--- a/SafeExamBrowser.UserInterface.Shared/SafeExamBrowser.UserInterface.Shared.csproj
+++ b/SafeExamBrowser.UserInterface.Shared/SafeExamBrowser.UserInterface.Shared.csproj
@@ -51,6 +51,7 @@
+
@@ -67,7 +68,6 @@
-
diff --git a/SafeExamBrowser.UserInterface.Shared/Utilities/IconResourceLoader.cs b/SafeExamBrowser.UserInterface.Shared/Utilities/IconResourceLoader.cs
index 7b841dc6..475dff51 100644
--- a/SafeExamBrowser.UserInterface.Shared/Utilities/IconResourceLoader.cs
+++ b/SafeExamBrowser.UserInterface.Shared/Utilities/IconResourceLoader.cs
@@ -7,40 +7,45 @@
*/
using System;
+using System.Drawing;
+using System.Drawing.Imaging;
+using System.IO;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Markup;
-using System.Windows.Media;
using System.Windows.Media.Imaging;
using SafeExamBrowser.Core.Contracts;
+using Brushes = System.Windows.Media.Brushes;
+using Image = System.Windows.Controls.Image;
namespace SafeExamBrowser.UserInterface.Shared.Utilities
{
public static class IconResourceLoader
{
- public static UIElement Load(IIconResource resource)
+ public static UIElement Load(IconResource resource)
{
try
{
- if (resource.IsBitmapResource)
+ switch (resource.Type)
{
- return LoadBitmapResource(resource);
- }
- else if (resource.IsXamlResource)
- {
- return LoadXamlResource(resource);
+ case IconResourceType.Bitmap:
+ return LoadBitmapResource(resource);
+ case IconResourceType.Embedded:
+ return LoadEmbeddedResource(resource);
+ case IconResourceType.Xaml:
+ return LoadXamlResource(resource);
+ default:
+ throw new NotSupportedException($"Application icon resource of type '{resource.Type}' is not supported!");
}
}
catch (Exception)
{
return NotFoundSymbol();
}
-
- throw new NotSupportedException($"Application icon resource of type '{resource.GetType()}' is not supported!");
}
- private static UIElement LoadBitmapResource(IIconResource resource)
+ private static UIElement LoadBitmapResource(IconResource resource)
{
return new Image
{
@@ -48,7 +53,28 @@ namespace SafeExamBrowser.UserInterface.Shared.Utilities
};
}
- private static UIElement LoadXamlResource(IIconResource resource)
+ private static UIElement LoadEmbeddedResource(IconResource resource)
+ {
+ using (var stream = new MemoryStream())
+ {
+ var bitmap = new BitmapImage();
+
+ Icon.ExtractAssociatedIcon(resource.Uri.LocalPath).ToBitmap().Save(stream, ImageFormat.Png);
+
+ bitmap.BeginInit();
+ bitmap.StreamSource = stream;
+ bitmap.CacheOption = BitmapCacheOption.OnLoad;
+ bitmap.EndInit();
+ bitmap.Freeze();
+
+ return new Image
+ {
+ Source = bitmap
+ };
+ }
+ }
+
+ private static UIElement LoadXamlResource(IconResource resource)
{
using (var stream = Application.GetResourceStream(resource.Uri)?.Stream)
{
diff --git a/SafeExamBrowser.UserInterface.Shared/Utilities/XamlIconResource.cs b/SafeExamBrowser.UserInterface.Shared/Utilities/XamlIconResource.cs
deleted file mode 100644
index 97b33a15..00000000
--- a/SafeExamBrowser.UserInterface.Shared/Utilities/XamlIconResource.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET)
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- */
-
-using System;
-using SafeExamBrowser.Core.Contracts;
-
-namespace SafeExamBrowser.UserInterface.Shared.Utilities
-{
- public class XamlIconResource : IIconResource
- {
- public Uri Uri { get; private set; }
- public bool IsBitmapResource => false;
- public bool IsXamlResource => true;
-
- public XamlIconResource(Uri uri)
- {
- Uri = uri;
- }
- }
-}