diff --git a/SafeExamBrowser.Client/CompositionRoot.cs b/SafeExamBrowser.Client/CompositionRoot.cs index 5b9d7469..3da0622a 100644 --- a/SafeExamBrowser.Client/CompositionRoot.cs +++ b/SafeExamBrowser.Client/CompositionRoot.cs @@ -249,7 +249,7 @@ namespace SafeExamBrowser.Client private IOperation BuildProctoringOperation() { - var controller = new ProctoringController(context.AppConfig, new FileSystem(), ModuleLogger(nameof(ProctoringController)), uiFactory); + var controller = new ProctoringController(context.AppConfig, new FileSystem(), ModuleLogger(nameof(ProctoringController)), text, uiFactory); var operation = new ProctoringOperation(actionCenter, context, controller, logger, controller, taskbar, uiFactory); context.ProctoringController = controller; diff --git a/SafeExamBrowser.Client/Notifications/AboutNotification.cs b/SafeExamBrowser.Client/Notifications/AboutNotification.cs index 969b81be..afceb0a8 100644 --- a/SafeExamBrowser.Client/Notifications/AboutNotification.cs +++ b/SafeExamBrowser.Client/Notifications/AboutNotification.cs @@ -9,6 +9,7 @@ using System; using SafeExamBrowser.Configuration.Contracts; using SafeExamBrowser.Core.Contracts.Notifications; +using SafeExamBrowser.Core.Contracts.Notifications.Events; using SafeExamBrowser.Core.Contracts.Resources.Icons; using SafeExamBrowser.I18n.Contracts; using SafeExamBrowser.UserInterface.Contracts; @@ -27,6 +28,8 @@ namespace SafeExamBrowser.Client.Notifications public string Tooltip { get; } public IconResource IconResource { get; } + public event NotificationChangedEventHandler NotificationChanged { add { } remove { } } + public AboutNotification(AppConfig appConfig, IText text, IUserInterfaceFactory uiFactory) { this.appConfig = appConfig; diff --git a/SafeExamBrowser.Client/Notifications/LogNotification.cs b/SafeExamBrowser.Client/Notifications/LogNotification.cs index e45aac9f..759dc5dd 100644 --- a/SafeExamBrowser.Client/Notifications/LogNotification.cs +++ b/SafeExamBrowser.Client/Notifications/LogNotification.cs @@ -8,6 +8,7 @@ using System; using SafeExamBrowser.Core.Contracts.Notifications; +using SafeExamBrowser.Core.Contracts.Notifications.Events; using SafeExamBrowser.Core.Contracts.Resources.Icons; using SafeExamBrowser.I18n.Contracts; using SafeExamBrowser.Logging.Contracts; @@ -27,6 +28,8 @@ namespace SafeExamBrowser.Client.Notifications public string Tooltip { get; } public IconResource IconResource { get; } + public event NotificationChangedEventHandler NotificationChanged { add { } remove { } } + public LogNotification(ILogger logger, IText text, IUserInterfaceFactory uiFactory) { this.logger = logger; diff --git a/SafeExamBrowser.Client/Operations/ProctoringOperation.cs b/SafeExamBrowser.Client/Operations/ProctoringOperation.cs index 56d7588a..2de795ba 100644 --- a/SafeExamBrowser.Client/Operations/ProctoringOperation.cs +++ b/SafeExamBrowser.Client/Operations/ProctoringOperation.cs @@ -53,13 +53,13 @@ namespace SafeExamBrowser.Client.Operations logger.Info("Initializing proctoring..."); StatusChanged?.Invoke(TextKey.OperationStatus_InitializeProctoring); - var actionCenterControl = uiFactory.CreateNotificationControl(notification, Location.ActionCenter); - var taskbarControl = uiFactory.CreateNotificationControl(notification, Location.Taskbar); - controller.Initialize(Context.Settings.Proctoring); + actionCenter.AddNotificationControl(uiFactory.CreateNotificationControl(notification, Location.ActionCenter)); - actionCenter.AddNotificationControl(actionCenterControl); - taskbar.AddNotificationControl(taskbarControl); + if (Context.Settings.Proctoring.ShowTaskbarNotification) + { + taskbar.AddNotificationControl(uiFactory.CreateNotificationControl(notification, Location.Taskbar)); + } } return OperationResult.Success; diff --git a/SafeExamBrowser.Configuration/ConfigurationData/DataMapping/ProctoringDataMapper.cs b/SafeExamBrowser.Configuration/ConfigurationData/DataMapping/ProctoringDataMapper.cs index 0627fea0..5bf05afa 100644 --- a/SafeExamBrowser.Configuration/ConfigurationData/DataMapping/ProctoringDataMapper.cs +++ b/SafeExamBrowser.Configuration/ConfigurationData/DataMapping/ProctoringDataMapper.cs @@ -6,8 +6,10 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +using System; using System.Collections.Generic; using SafeExamBrowser.Settings; +using SafeExamBrowser.Settings.Proctoring; namespace SafeExamBrowser.Configuration.ConfigurationData.DataMapping { @@ -29,9 +31,28 @@ namespace SafeExamBrowser.Configuration.ConfigurationData.DataMapping case Keys.Proctoring.JitsiMeet.Token: MapJitsiMeetToken(settings, value); break; + case Keys.Proctoring.ShowTaskbarNotification: + MapShowTaskbarNotification(settings, value); + break; + case Keys.Proctoring.WindowVisibility: + MapWindowVisibility(settings, value); + break; } } + internal override void MapGlobal(IDictionary rawData, AppSettings settings) + { + MapProctoringEnabled(rawData, settings); + } + + private void MapProctoringEnabled(IDictionary rawData, AppSettings settings) + { + var jitsiEnabled = rawData.TryGetValue(Keys.Proctoring.JitsiMeet.Enabled, out var v) && v is bool b && b; + var zoomEnabled = rawData.TryGetValue(Keys.Proctoring.Zoom.Enabled, out v) && v is bool b2 && b2; + + settings.Proctoring.Enabled = jitsiEnabled || zoomEnabled; + } + private void MapJitsiMeetRoomName(AppSettings settings, object value) { if (value is string name) @@ -64,17 +85,39 @@ namespace SafeExamBrowser.Configuration.ConfigurationData.DataMapping } } - internal override void MapGlobal(IDictionary rawData, AppSettings settings) + private void MapShowTaskbarNotification(AppSettings settings, object value) { - MapProctoringEnabled(rawData, settings); + if (value is bool show) + { + settings.Proctoring.ShowTaskbarNotification = show; + } } - private void MapProctoringEnabled(IDictionary rawData, AppSettings settings) + private void MapWindowVisibility(AppSettings settings, object value) { - var jitsiEnabled = rawData.TryGetValue(Keys.Proctoring.JitsiMeet.Enabled, out var v) && v is bool b && b; - var zoomEnabled = rawData.TryGetValue(Keys.Proctoring.Zoom.Enabled, out v) && v is bool b2 && b2; + const int HIDDEN = 0; + const int ALLOW_SHOW = 1; + const int ALLOW_HIDE = 2; + const int VISIBLE = 3; - settings.Proctoring.Enabled = jitsiEnabled || zoomEnabled; + if (value is int visibility) + { + switch (visibility) + { + case HIDDEN: + settings.Proctoring.WindowVisibility = WindowVisibility.Hidden; + break; + case ALLOW_SHOW: + settings.Proctoring.WindowVisibility = WindowVisibility.AllowToShow; + break; + case ALLOW_HIDE: + settings.Proctoring.WindowVisibility = WindowVisibility.AllowToHide; + break; + case VISIBLE: + settings.Proctoring.WindowVisibility = WindowVisibility.Visible; + break; + } + } } } } diff --git a/SafeExamBrowser.Configuration/ConfigurationData/DataProcessor.cs b/SafeExamBrowser.Configuration/ConfigurationData/DataProcessor.cs index 05ff26b6..ffb84b28 100644 --- a/SafeExamBrowser.Configuration/ConfigurationData/DataProcessor.cs +++ b/SafeExamBrowser.Configuration/ConfigurationData/DataProcessor.cs @@ -22,6 +22,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData AllowBrowserToolbarForReloading(rawData, settings); CalculateConfigurationKey(rawData, settings); HandleBrowserHomeFunctionality(settings); + InitializeProctoringSettings(settings); RemoveLegacyBrowsers(settings); } @@ -64,6 +65,11 @@ namespace SafeExamBrowser.Configuration.ConfigurationData settings.Browser.HomePasswordHash = settings.Security.QuitPasswordHash; } + private void InitializeProctoringSettings(AppSettings settings) + { + settings.Proctoring.Enabled = settings.Proctoring.JitsiMeet.Enabled || settings.Proctoring.Zoom.Enabled; + } + private void RemoveLegacyBrowsers(AppSettings settings) { var legacyBrowsers = new List(); diff --git a/SafeExamBrowser.Configuration/ConfigurationData/DataValues.cs b/SafeExamBrowser.Configuration/ConfigurationData/DataValues.cs index 8ca72a5f..72ac759f 100644 --- a/SafeExamBrowser.Configuration/ConfigurationData/DataValues.cs +++ b/SafeExamBrowser.Configuration/ConfigurationData/DataValues.cs @@ -176,6 +176,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData settings.Mouse.AllowRightButton = true; settings.Proctoring.Enabled = false; + settings.Proctoring.ShowTaskbarNotification = true; settings.Proctoring.WindowVisibility = WindowVisibility.Hidden; settings.Security.AllowApplicationLogAccess = false; diff --git a/SafeExamBrowser.Configuration/ConfigurationData/Keys.cs b/SafeExamBrowser.Configuration/ConfigurationData/Keys.cs index 37d2a90c..1a64fcb5 100644 --- a/SafeExamBrowser.Configuration/ConfigurationData/Keys.cs +++ b/SafeExamBrowser.Configuration/ConfigurationData/Keys.cs @@ -216,6 +216,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData internal static class Proctoring { + internal const string ShowTaskbarNotification = "showProctoringViewButton"; internal const string WindowVisibility = "remoteProctoringViewShow"; internal static class JitsiMeet diff --git a/SafeExamBrowser.Core.Contracts/Notifications/Events/NotificationChangedEventHandler.cs b/SafeExamBrowser.Core.Contracts/Notifications/Events/NotificationChangedEventHandler.cs new file mode 100644 index 00000000..b921ff38 --- /dev/null +++ b/SafeExamBrowser.Core.Contracts/Notifications/Events/NotificationChangedEventHandler.cs @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2021 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.Notifications.Events +{ + /// + /// Indicates that a notification has changed. + /// + public delegate void NotificationChangedEventHandler(); +} diff --git a/SafeExamBrowser.Core.Contracts/Notifications/INotification.cs b/SafeExamBrowser.Core.Contracts/Notifications/INotification.cs index e50f9bb1..c91d58f6 100644 --- a/SafeExamBrowser.Core.Contracts/Notifications/INotification.cs +++ b/SafeExamBrowser.Core.Contracts/Notifications/INotification.cs @@ -6,6 +6,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +using SafeExamBrowser.Core.Contracts.Notifications.Events; using SafeExamBrowser.Core.Contracts.Resources.Icons; namespace SafeExamBrowser.Core.Contracts.Notifications @@ -25,6 +26,11 @@ namespace SafeExamBrowser.Core.Contracts.Notifications /// IconResource IconResource { get; } + /// + /// Event fired when the notification has changed. + /// + event NotificationChangedEventHandler NotificationChanged; + /// /// Executes the notification functionality. /// diff --git a/SafeExamBrowser.Core.Contracts/SafeExamBrowser.Core.Contracts.csproj b/SafeExamBrowser.Core.Contracts/SafeExamBrowser.Core.Contracts.csproj index 0a7781dd..065a1548 100644 --- a/SafeExamBrowser.Core.Contracts/SafeExamBrowser.Core.Contracts.csproj +++ b/SafeExamBrowser.Core.Contracts/SafeExamBrowser.Core.Contracts.csproj @@ -54,6 +54,7 @@ + diff --git a/SafeExamBrowser.I18n.Contracts/TextKey.cs b/SafeExamBrowser.I18n.Contracts/TextKey.cs index 45a20622..af528476 100644 --- a/SafeExamBrowser.I18n.Contracts/TextKey.cs +++ b/SafeExamBrowser.I18n.Contracts/TextKey.cs @@ -129,6 +129,8 @@ namespace SafeExamBrowser.I18n.Contracts MessageBox_YesButton, Notification_AboutTooltip, Notification_LogTooltip, + // TODO + Notification_ProctoringTooltip, OperationStatus_CloseRuntimeConnection, OperationStatus_EmptyClipboard, OperationStatus_FinalizeApplications, diff --git a/SafeExamBrowser.Proctoring/ProctoringController.cs b/SafeExamBrowser.Proctoring/ProctoringController.cs index abd5ac07..177ac351 100644 --- a/SafeExamBrowser.Proctoring/ProctoringController.cs +++ b/SafeExamBrowser.Proctoring/ProctoringController.cs @@ -11,7 +11,9 @@ using System.IO; using System.Reflection; using SafeExamBrowser.Configuration.Contracts; using SafeExamBrowser.Core.Contracts.Notifications; +using SafeExamBrowser.Core.Contracts.Notifications.Events; using SafeExamBrowser.Core.Contracts.Resources.Icons; +using SafeExamBrowser.I18n.Contracts; using SafeExamBrowser.Logging.Contracts; using SafeExamBrowser.Proctoring.Contracts; using SafeExamBrowser.Settings.Proctoring; @@ -26,21 +28,28 @@ namespace SafeExamBrowser.Proctoring private readonly AppConfig appConfig; private readonly IFileSystem fileSystem; private readonly IModuleLogger logger; + private readonly IText text; private readonly IUserInterfaceFactory uiFactory; private string filePath; private IProctoringWindow window; private ProctoringSettings settings; - public string Tooltip => "TODO!!!"; - public IconResource IconResource => new XamlIconResource(); + public string Tooltip { get; } + public IconResource IconResource { get; set; } - public ProctoringController(AppConfig appConfig, IFileSystem fileSystem, IModuleLogger logger, IUserInterfaceFactory uiFactory) + public event NotificationChangedEventHandler NotificationChanged; + + public ProctoringController(AppConfig appConfig, IFileSystem fileSystem, IModuleLogger logger, IText text, IUserInterfaceFactory uiFactory) { this.appConfig = appConfig; this.fileSystem = fileSystem; this.logger = logger; + this.text = text; this.uiFactory = uiFactory; + + IconResource = new XamlIconResource { Uri = new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Desktop;component/Images/ProctoringNotification_Inactive.xaml") }; + Tooltip = text.Get(TextKey.Notification_ProctoringTooltip); } public void Activate() @@ -81,6 +90,9 @@ namespace SafeExamBrowser.Proctoring } logger.Info($"Initialized proctoring with {(settings.JitsiMeet.Enabled ? "Jitsi Meet" : "Zoom")}."); + + IconResource = new XamlIconResource { Uri = new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Desktop;component/Images/ProctoringNotification_Active.xaml") }; + NotificationChanged?.Invoke(); } else { diff --git a/SafeExamBrowser.Proctoring/SafeExamBrowser.Proctoring.csproj b/SafeExamBrowser.Proctoring/SafeExamBrowser.Proctoring.csproj index 9f888d56..1954bffa 100644 --- a/SafeExamBrowser.Proctoring/SafeExamBrowser.Proctoring.csproj +++ b/SafeExamBrowser.Proctoring/SafeExamBrowser.Proctoring.csproj @@ -82,6 +82,10 @@ {fe0e1224-b447-4b14-81e7-ed7d84822aa0} SafeExamBrowser.Core.Contracts + + {1858ddf3-bc2a-4bff-b663-4ce2ffeb8b7d} + SafeExamBrowser.I18n.Contracts + {64ea30fb-11d4-436a-9c2b-88566285363e} SafeExamBrowser.Logging.Contracts diff --git a/SafeExamBrowser.Settings/Proctoring/ProctoringSettings.cs b/SafeExamBrowser.Settings/Proctoring/ProctoringSettings.cs index ae816069..00ebb799 100644 --- a/SafeExamBrowser.Settings/Proctoring/ProctoringSettings.cs +++ b/SafeExamBrowser.Settings/Proctoring/ProctoringSettings.cs @@ -26,6 +26,11 @@ namespace SafeExamBrowser.Settings.Proctoring /// public JitsiMeetSettings JitsiMeet { get; set; } + /// + /// Determines whether the proctoring notification will be shown in the taskbar. + /// + public bool ShowTaskbarNotification { get; set; } + /// /// Determines the visibility of the proctoring window. /// diff --git a/SafeExamBrowser.UserInterface.Desktop/Controls/ActionCenter/NotificationButton.xaml.cs b/SafeExamBrowser.UserInterface.Desktop/Controls/ActionCenter/NotificationButton.xaml.cs index ade141b5..60206849 100644 --- a/SafeExamBrowser.UserInterface.Desktop/Controls/ActionCenter/NotificationButton.xaml.cs +++ b/SafeExamBrowser.UserInterface.Desktop/Controls/ActionCenter/NotificationButton.xaml.cs @@ -24,6 +24,7 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls.ActionCenter InitializeComponent(); InitializeNotification(); + UpdateNotification(); } private void IconButton_Click(object sender, RoutedEventArgs e) @@ -32,6 +33,11 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls.ActionCenter } private void InitializeNotification() + { + notification.NotificationChanged += () => Dispatcher.Invoke(UpdateNotification); + } + + private void UpdateNotification() { Icon.Content = IconResourceLoader.Load(notification.IconResource); IconButton.ToolTip = notification.Tooltip; diff --git a/SafeExamBrowser.UserInterface.Desktop/Controls/Taskbar/NotificationButton.xaml.cs b/SafeExamBrowser.UserInterface.Desktop/Controls/Taskbar/NotificationButton.xaml.cs index ed3d8abe..a6804119 100644 --- a/SafeExamBrowser.UserInterface.Desktop/Controls/Taskbar/NotificationButton.xaml.cs +++ b/SafeExamBrowser.UserInterface.Desktop/Controls/Taskbar/NotificationButton.xaml.cs @@ -24,6 +24,7 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls.Taskbar InitializeComponent(); InitializeNotification(); + UpdateNotification(); } private void IconButton_Click(object sender, RoutedEventArgs e) @@ -32,6 +33,11 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls.Taskbar } private void InitializeNotification() + { + notification.NotificationChanged += () => Dispatcher.Invoke(UpdateNotification); + } + + private void UpdateNotification() { IconButton.ToolTip = notification.Tooltip; IconButton.Content = IconResourceLoader.Load(notification.IconResource); diff --git a/SafeExamBrowser.UserInterface.Desktop/Images/ProctoringNotification_Active.xaml b/SafeExamBrowser.UserInterface.Desktop/Images/ProctoringNotification_Active.xaml new file mode 100644 index 00000000..e53ef338 --- /dev/null +++ b/SafeExamBrowser.UserInterface.Desktop/Images/ProctoringNotification_Active.xaml @@ -0,0 +1,12 @@ + + + + + + + + + + diff --git a/SafeExamBrowser.UserInterface.Desktop/Images/ProctoringNotification_Inactive.xaml b/SafeExamBrowser.UserInterface.Desktop/Images/ProctoringNotification_Inactive.xaml new file mode 100644 index 00000000..f01d3c34 --- /dev/null +++ b/SafeExamBrowser.UserInterface.Desktop/Images/ProctoringNotification_Inactive.xaml @@ -0,0 +1,9 @@ + + + + + + + diff --git a/SafeExamBrowser.UserInterface.Desktop/SafeExamBrowser.UserInterface.Desktop.csproj b/SafeExamBrowser.UserInterface.Desktop/SafeExamBrowser.UserInterface.Desktop.csproj index 2d7e28c8..1e66c46b 100644 --- a/SafeExamBrowser.UserInterface.Desktop/SafeExamBrowser.UserInterface.Desktop.csproj +++ b/SafeExamBrowser.UserInterface.Desktop/SafeExamBrowser.UserInterface.Desktop.csproj @@ -185,6 +185,14 @@ + + Designer + MSBuild:Compile + + + MSBuild:Compile + Designer + Designer MSBuild:Compile diff --git a/SafeExamBrowser.UserInterface.Mobile/Controls/ActionCenter/NotificationButton.xaml.cs b/SafeExamBrowser.UserInterface.Mobile/Controls/ActionCenter/NotificationButton.xaml.cs index ce129e49..f9a9b8a3 100644 --- a/SafeExamBrowser.UserInterface.Mobile/Controls/ActionCenter/NotificationButton.xaml.cs +++ b/SafeExamBrowser.UserInterface.Mobile/Controls/ActionCenter/NotificationButton.xaml.cs @@ -24,6 +24,7 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls.ActionCenter InitializeComponent(); InitializeNotification(); + UpdateNotification(); } private void IconButton_Click(object sender, RoutedEventArgs e) @@ -32,6 +33,11 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls.ActionCenter } private void InitializeNotification() + { + notification.NotificationChanged += () => Dispatcher.Invoke(UpdateNotification); + } + + private void UpdateNotification() { Icon.Content = IconResourceLoader.Load(notification.IconResource); IconButton.ToolTip = notification.Tooltip; diff --git a/SafeExamBrowser.UserInterface.Mobile/Controls/Taskbar/NotificationButton.xaml.cs b/SafeExamBrowser.UserInterface.Mobile/Controls/Taskbar/NotificationButton.xaml.cs index 2d35d09a..87838f26 100644 --- a/SafeExamBrowser.UserInterface.Mobile/Controls/Taskbar/NotificationButton.xaml.cs +++ b/SafeExamBrowser.UserInterface.Mobile/Controls/Taskbar/NotificationButton.xaml.cs @@ -24,6 +24,7 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls.Taskbar InitializeComponent(); InitializeNotification(); + UpdateNotification(); } private void IconButton_Click(object sender, RoutedEventArgs e) @@ -32,6 +33,11 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls.Taskbar } private void InitializeNotification() + { + notification.NotificationChanged += () => Dispatcher.Invoke(UpdateNotification); + } + + private void UpdateNotification() { IconButton.ToolTip = notification.Tooltip; IconButton.Content = IconResourceLoader.Load(notification.IconResource);