diff --git a/SafeExamBrowser.Configuration/ConfigurationData/DataValues.cs b/SafeExamBrowser.Configuration/ConfigurationData/DataValues.cs
index 9383700d..8ca72a5f 100644
--- a/SafeExamBrowser.Configuration/ConfigurationData/DataValues.cs
+++ b/SafeExamBrowser.Configuration/ConfigurationData/DataValues.cs
@@ -16,6 +16,7 @@ using SafeExamBrowser.Settings;
using SafeExamBrowser.Settings.Browser;
using SafeExamBrowser.Settings.Browser.Proxy;
using SafeExamBrowser.Settings.Logging;
+using SafeExamBrowser.Settings.Proctoring;
using SafeExamBrowser.Settings.Security;
using SafeExamBrowser.Settings.Service;
using SafeExamBrowser.Settings.UserInterface;
@@ -174,6 +175,9 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
settings.Mouse.AllowMiddleButton = false;
settings.Mouse.AllowRightButton = true;
+ settings.Proctoring.Enabled = false;
+ settings.Proctoring.WindowVisibility = WindowVisibility.Hidden;
+
settings.Security.AllowApplicationLogAccess = false;
settings.Security.AllowTermination = true;
settings.Security.AllowReconfiguration = false;
diff --git a/SafeExamBrowser.Configuration/ConfigurationData/Keys.cs b/SafeExamBrowser.Configuration/ConfigurationData/Keys.cs
index 899f464f..37d2a90c 100644
--- a/SafeExamBrowser.Configuration/ConfigurationData/Keys.cs
+++ b/SafeExamBrowser.Configuration/ConfigurationData/Keys.cs
@@ -216,6 +216,8 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
internal static class Proctoring
{
+ internal const string WindowVisibility = "remoteProctoringViewShow";
+
internal static class JitsiMeet
{
internal const string Enabled = "jitsiMeetEnable";
diff --git a/SafeExamBrowser.Proctoring/JitsiMeet/index.html b/SafeExamBrowser.Proctoring/JitsiMeet/index.html
index 94532f2e..fa6faae1 100644
--- a/SafeExamBrowser.Proctoring/JitsiMeet/index.html
+++ b/SafeExamBrowser.Proctoring/JitsiMeet/index.html
@@ -9,12 +9,23 @@
var domain = "%%_DOMAIN_%%";
var options = {
configOverwrite: {
+ disable1On1Mode: true,
startAudioOnly: false,
startWithAudioMuted: true,
- startWithVideoMuted: false,
- disable1On1Mode: true
+ startWithVideoMuted: false
},
height: "100%",
+ interfaceConfigOverwrite: {
+ JITSI_WATERMARK_LINK: '',
+ SHOW_JITSI_WATERMARK: false,
+ TOOLBAR_BUTTONS: [
+ 'microphone', 'camera', 'closedcaptions', 'desktop', 'embedmeeting', 'fullscreen',
+ 'fodeviceselection', 'hangup', 'profile', 'chat', 'recording',
+ 'livestreaming', 'etherpad', /*'sharedvideo',*/ 'settings', 'raisehand',
+ 'videoquality', 'filmstrip', 'invite', 'feedback', 'stats', 'shortcuts',
+ 'tileview', 'select-background', 'download', 'help', 'mute-everyone', 'mute-video-everyone', 'security'
+ ]
+ },
jwt: "%%_TOKEN_%%",
parentNode: document.querySelector('#placeholder'),
roomName: "%%_ROOM_NAME_%%",
diff --git a/SafeExamBrowser.Proctoring/ProctoringControl.cs b/SafeExamBrowser.Proctoring/ProctoringControl.cs
index b01fb253..c446fada 100644
--- a/SafeExamBrowser.Proctoring/ProctoringControl.cs
+++ b/SafeExamBrowser.Proctoring/ProctoringControl.cs
@@ -10,6 +10,7 @@ using Microsoft.Web.WebView2.Core;
using Microsoft.Web.WebView2.Wpf;
using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.UserInterface.Contracts.Proctoring;
+using SafeExamBrowser.UserInterface.Contracts.Proctoring.Events;
namespace SafeExamBrowser.Proctoring
{
@@ -17,12 +18,34 @@ namespace SafeExamBrowser.Proctoring
{
private readonly ILogger logger;
+ public event FullScreenChangedEventHandler FullScreenChanged;
+
internal ProctoringControl(ILogger logger)
{
this.logger = logger;
CoreWebView2InitializationCompleted += ProctoringControl_CoreWebView2InitializationCompleted;
}
+ private void ProctoringControl_CoreWebView2InitializationCompleted(object sender, CoreWebView2InitializationCompletedEventArgs e)
+ {
+ if (e.IsSuccess)
+ {
+ CoreWebView2.ContainsFullScreenElementChanged += CoreWebView2_ContainsFullScreenElementChanged;
+ CoreWebView2.PermissionRequested += CoreWebView2_PermissionRequested;
+ logger.Info("Successfully initialized.");
+ }
+ else
+ {
+ logger.Error("Failed to initialize!", e.InitializationException);
+ }
+ }
+
+ private void CoreWebView2_ContainsFullScreenElementChanged(object sender, object e)
+ {
+ FullScreenChanged?.Invoke(CoreWebView2.ContainsFullScreenElement);
+ logger.Debug($"Full screen ${(CoreWebView2.ContainsFullScreenElement ? "activated" : "deactivated")}.");
+ }
+
private void CoreWebView2_PermissionRequested(object sender, CoreWebView2PermissionRequestedEventArgs e)
{
if (e.PermissionKind == CoreWebView2PermissionKind.Camera || e.PermissionKind == CoreWebView2PermissionKind.Microphone)
@@ -35,18 +58,5 @@ namespace SafeExamBrowser.Proctoring
logger.Info($"Denied access to {e.PermissionKind}.");
}
}
-
- private void ProctoringControl_CoreWebView2InitializationCompleted(object sender, CoreWebView2InitializationCompletedEventArgs e)
- {
- if (e.IsSuccess)
- {
- CoreWebView2.PermissionRequested += CoreWebView2_PermissionRequested;
- logger.Info("Successfully initialized.");
- }
- else
- {
- logger.Error("Failed to initialize!", e.InitializationException);
- }
- }
}
}
diff --git a/SafeExamBrowser.Proctoring/ProctoringController.cs b/SafeExamBrowser.Proctoring/ProctoringController.cs
index a62f2374..abd5ac07 100644
--- a/SafeExamBrowser.Proctoring/ProctoringController.cs
+++ b/SafeExamBrowser.Proctoring/ProctoringController.cs
@@ -30,6 +30,7 @@ namespace SafeExamBrowser.Proctoring
private string filePath;
private IProctoringWindow window;
+ private ProctoringSettings settings;
public string Tooltip => "TODO!!!";
public IconResource IconResource => new XamlIconResource();
@@ -44,11 +45,20 @@ namespace SafeExamBrowser.Proctoring
public void Activate()
{
- window?.Show();
+ if (settings.WindowVisibility == WindowVisibility.Visible)
+ {
+ window?.BringToForeground();
+ }
+ else if (settings.WindowVisibility == WindowVisibility.AllowToHide || settings.WindowVisibility == WindowVisibility.AllowToShow)
+ {
+ window.Toggle();
+ }
}
public void Initialize(ProctoringSettings settings)
{
+ this.settings = settings;
+
if (settings.JitsiMeet.Enabled || settings.Zoom.Enabled)
{
var content = LoadContent(settings);
@@ -63,7 +73,12 @@ namespace SafeExamBrowser.Proctoring
});
window = uiFactory.CreateProctoringWindow(control);
- window.Show();
+
+ if (settings.WindowVisibility == WindowVisibility.AllowToHide || settings.WindowVisibility == WindowVisibility.Visible)
+ {
+ window.SetTitle(settings.JitsiMeet.Enabled ? settings.JitsiMeet.Subject : settings.Zoom.UserName);
+ window.Show();
+ }
logger.Info($"Initialized proctoring with {(settings.JitsiMeet.Enabled ? "Jitsi Meet" : "Zoom")}.");
}
@@ -73,9 +88,13 @@ namespace SafeExamBrowser.Proctoring
}
}
- public void Terminate()
+ void INotification.Terminate()
{
window?.Close();
+ }
+
+ void IProctoringController.Terminate()
+ {
fileSystem.Delete(filePath);
}
diff --git a/SafeExamBrowser.Settings/Proctoring/ProctoringSettings.cs b/SafeExamBrowser.Settings/Proctoring/ProctoringSettings.cs
index cb14ccb8..ae816069 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 the visibility of the proctoring window.
+ ///
+ public WindowVisibility WindowVisibility { get; set; }
+
///
/// All settings for remote proctoring with Zoom.
///
diff --git a/SafeExamBrowser.Settings/Proctoring/WindowVisibility.cs b/SafeExamBrowser.Settings/Proctoring/WindowVisibility.cs
new file mode 100644
index 00000000..82c85b78
--- /dev/null
+++ b/SafeExamBrowser.Settings/Proctoring/WindowVisibility.cs
@@ -0,0 +1,36 @@
+/*
+ * 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.Settings.Proctoring
+{
+ ///
+ /// Defines all possible visibility states for the proctoring window.
+ ///
+ public enum WindowVisibility
+ {
+ ///
+ /// The proctoring window is hidden and cannot be shown by the user.
+ ///
+ Hidden,
+
+ ///
+ /// The proctoring window is initially hidden but may be shown by the user.
+ ///
+ AllowToShow,
+
+ ///
+ /// The proctoring window is initially visible but may be hidden by the user.
+ ///
+ AllowToHide,
+
+ ///
+ /// The proctoring window is always visible and cannot be hidden by the user.
+ ///
+ Visible
+ }
+}
diff --git a/SafeExamBrowser.Settings/SafeExamBrowser.Settings.csproj b/SafeExamBrowser.Settings/SafeExamBrowser.Settings.csproj
index 6780fbb9..afd1c6c3 100644
--- a/SafeExamBrowser.Settings/SafeExamBrowser.Settings.csproj
+++ b/SafeExamBrowser.Settings/SafeExamBrowser.Settings.csproj
@@ -73,6 +73,7 @@
+
diff --git a/SafeExamBrowser.UserInterface.Contracts/Proctoring/Events/FullScreenChangedEventHandler.cs b/SafeExamBrowser.UserInterface.Contracts/Proctoring/Events/FullScreenChangedEventHandler.cs
new file mode 100644
index 00000000..110c3126
--- /dev/null
+++ b/SafeExamBrowser.UserInterface.Contracts/Proctoring/Events/FullScreenChangedEventHandler.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.UserInterface.Contracts.Proctoring.Events
+{
+ ///
+ /// Indicates that the full screen state has changed.
+ ///
+ public delegate void FullScreenChangedEventHandler(bool fullScreen);
+}
diff --git a/SafeExamBrowser.UserInterface.Contracts/Proctoring/IProctoringControl.cs b/SafeExamBrowser.UserInterface.Contracts/Proctoring/IProctoringControl.cs
index c6717b54..0720f1c8 100644
--- a/SafeExamBrowser.UserInterface.Contracts/Proctoring/IProctoringControl.cs
+++ b/SafeExamBrowser.UserInterface.Contracts/Proctoring/IProctoringControl.cs
@@ -6,13 +6,19 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
+using SafeExamBrowser.UserInterface.Contracts.Proctoring.Events;
+
namespace SafeExamBrowser.UserInterface.Contracts.Proctoring
{
///
- ///
+ /// Defines the functionality of a proctoring control, i.e. a web view running a WebRTC-enabled web application, which normally is embedded in
+ /// a .
///
public interface IProctoringControl
{
-
+ ///
+ /// Event fired when the full screen state changed.
+ ///
+ event FullScreenChangedEventHandler FullScreenChanged;
}
}
diff --git a/SafeExamBrowser.UserInterface.Contracts/Proctoring/IProctoringWindow.cs b/SafeExamBrowser.UserInterface.Contracts/Proctoring/IProctoringWindow.cs
index d90e8dee..cb1e8ce9 100644
--- a/SafeExamBrowser.UserInterface.Contracts/Proctoring/IProctoringWindow.cs
+++ b/SafeExamBrowser.UserInterface.Contracts/Proctoring/IProctoringWindow.cs
@@ -11,10 +11,18 @@ using SafeExamBrowser.UserInterface.Contracts.Windows;
namespace SafeExamBrowser.UserInterface.Contracts.Proctoring
{
///
- ///
+ /// Defines the functionality of a proctoring window.
///
public interface IProctoringWindow : IWindow
{
+ ///
+ /// Sets the window title to the given value.
+ ///
+ void SetTitle(string title);
+ ///
+ /// Toggles the window visibility.
+ ///
+ void Toggle();
}
}
diff --git a/SafeExamBrowser.UserInterface.Contracts/SafeExamBrowser.UserInterface.Contracts.csproj b/SafeExamBrowser.UserInterface.Contracts/SafeExamBrowser.UserInterface.Contracts.csproj
index f9ea9dc9..eaa90849 100644
--- a/SafeExamBrowser.UserInterface.Contracts/SafeExamBrowser.UserInterface.Contracts.csproj
+++ b/SafeExamBrowser.UserInterface.Contracts/SafeExamBrowser.UserInterface.Contracts.csproj
@@ -67,6 +67,7 @@
+
diff --git a/SafeExamBrowser.UserInterface.Desktop/Windows/ProctoringWindow.xaml b/SafeExamBrowser.UserInterface.Desktop/Windows/ProctoringWindow.xaml
index 7a62d514..6433deaa 100644
--- a/SafeExamBrowser.UserInterface.Desktop/Windows/ProctoringWindow.xaml
+++ b/SafeExamBrowser.UserInterface.Desktop/Windows/ProctoringWindow.xaml
@@ -4,5 +4,5 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:SafeExamBrowser.UserInterface.Desktop.Windows"
- mc:Ignorable="d" Height="250" Width="350" Topmost="True" WindowStyle="None">
+ mc:Ignorable="d" Height="250" Width="350" MinHeight="250" MinWidth="250" Topmost="True" WindowStyle="ToolWindow">
diff --git a/SafeExamBrowser.UserInterface.Desktop/Windows/ProctoringWindow.xaml.cs b/SafeExamBrowser.UserInterface.Desktop/Windows/ProctoringWindow.xaml.cs
index 81713305..5594de78 100644
--- a/SafeExamBrowser.UserInterface.Desktop/Windows/ProctoringWindow.xaml.cs
+++ b/SafeExamBrowser.UserInterface.Desktop/Windows/ProctoringWindow.xaml.cs
@@ -11,6 +11,7 @@ using System.Windows;
using SafeExamBrowser.UserInterface.Contracts.Proctoring;
using SafeExamBrowser.UserInterface.Contracts.Windows;
using SafeExamBrowser.UserInterface.Contracts.Windows.Events;
+using SafeExamBrowser.UserInterface.Shared.Utilities;
namespace SafeExamBrowser.UserInterface.Desktop.Windows
{
@@ -47,6 +48,7 @@ namespace SafeExamBrowser.UserInterface.Desktop.Windows
{
Dispatcher.Invoke(() =>
{
+ Closing -= ProctoringWindow_Closing;
closing?.Invoke();
base.Close();
});
@@ -57,26 +59,67 @@ namespace SafeExamBrowser.UserInterface.Desktop.Windows
Dispatcher.Invoke(base.Hide);
}
+ public void SetTitle(string title)
+ {
+ Dispatcher.Invoke(() => Title = title);
+ }
+
public new void Show()
{
Dispatcher.Invoke(base.Show);
}
- private void ProctoringWindow_Closing(object sender, CancelEventArgs e)
+ public void Toggle()
{
- closing?.Invoke();
+ Dispatcher.Invoke(() =>
+ {
+ if (Visibility == Visibility.Visible)
+ {
+ base.Hide();
+ }
+ else
+ {
+ base.Show();
+ }
+ });
}
- private void InitializeWindow(object control)
+ private void InitializeWindow(IProctoringControl control)
{
if (control is UIElement element)
{
Content = element;
+ control.FullScreenChanged += Control_FullScreenChanged;
}
Closing += ProctoringWindow_Closing;
- Top = SystemParameters.WorkArea.Height - Height;
- Left = SystemParameters.WorkArea.Width - Width;
+ Loaded += ProctoringWindow_Loaded;
+ Top = SystemParameters.WorkArea.Height - Height - 15;
+ Left = SystemParameters.WorkArea.Width - Width - 20;
+ }
+
+ private void Control_FullScreenChanged(bool fullScreen)
+ {
+ if (fullScreen)
+ {
+ WindowState = WindowState.Maximized;
+ WindowStyle = WindowStyle.None;
+ }
+ else
+ {
+ WindowState = WindowState.Normal;
+ WindowStyle = WindowStyle.ToolWindow;
+ }
+ }
+
+ private void ProctoringWindow_Closing(object sender, CancelEventArgs e)
+ {
+ e.Cancel = true;
+ }
+
+ private void ProctoringWindow_Loaded(object sender, RoutedEventArgs e)
+ {
+ this.HideCloseButton();
}
}
}
diff --git a/SafeExamBrowser.UserInterface.Shared/Utilities/WindowExtensions.cs b/SafeExamBrowser.UserInterface.Shared/Utilities/WindowExtensions.cs
index c93c0a54..6258af4b 100644
--- a/SafeExamBrowser.UserInterface.Shared/Utilities/WindowExtensions.cs
+++ b/SafeExamBrowser.UserInterface.Shared/Utilities/WindowExtensions.cs
@@ -15,10 +15,12 @@ namespace SafeExamBrowser.UserInterface.Shared.Utilities
{
public static class WindowExtensions
{
+ private const int GWL_STYLE = -16;
private const uint MF_BYCOMMAND = 0x00000000;
private const uint MF_GRAYED = 0x00000001;
private const uint MF_ENABLED = 0x00000000;
private const uint SC_CLOSE = 0xF060;
+ private const int WS_SYSMENU = 0x80000;
public static void DisableCloseButton(this Window window)
{
@@ -31,10 +33,24 @@ namespace SafeExamBrowser.UserInterface.Shared.Utilities
}
}
+ public static void HideCloseButton(this Window window)
+ {
+ var helper = new WindowInteropHelper(window);
+ var style = GetWindowLong(helper.Handle, GWL_STYLE) & ~WS_SYSMENU;
+
+ SetWindowLong(helper.Handle, GWL_STYLE, style);
+ }
+
+ [DllImport("user32.dll")]
+ private static extern bool EnableMenuItem(IntPtr hMenu, uint uIDEnableItem, uint uEnable);
+
[DllImport("user32.dll")]
private static extern IntPtr GetSystemMenu(IntPtr hWnd, bool bRevert);
[DllImport("user32.dll")]
- private static extern bool EnableMenuItem(IntPtr hMenu, uint uIDEnableItem, uint uEnable);
+ private static extern int GetWindowLong(IntPtr hWnd, int nIndex);
+
+ [DllImport("user32.dll")]
+ private static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);
}
}