Update Safe Exam Browser Patch to 3.10.0.826

This commit is contained in:
2025-09-16 16:32:31 +02:00
parent 4827ae1afc
commit dd82d45ed8
320 changed files with 8445 additions and 5295 deletions

View File

@@ -0,0 +1,115 @@
/*
* Copyright (c) 2025 ETH Zürich, IT Services
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
using SafeExamBrowser.Applications.Contracts;
using SafeExamBrowser.Core.Contracts.Notifications;
using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.Server.Contracts;
using SafeExamBrowser.Settings.Server;
using SafeExamBrowser.SystemComponents.Contracts.Audio;
using SafeExamBrowser.SystemComponents.Contracts.Keyboard;
using SafeExamBrowser.SystemComponents.Contracts.Network;
using SafeExamBrowser.SystemComponents.Contracts.PowerSupply;
using SafeExamBrowser.UserInterface.Contracts.Shell;
namespace SafeExamBrowser.UserInterface.Mobile
{
internal class ControlFactory
{
private readonly IText text;
internal ControlFactory(IText text)
{
this.text = text;
}
internal IApplicationControl CreateApplicationControl(IApplication<IApplicationWindow> application, Location location)
{
if (location == Location.ActionCenter)
{
return new Controls.ActionCenter.ApplicationControl(application);
}
else
{
return new Controls.Taskbar.ApplicationControl(application);
}
}
internal ISystemControl CreateAudioControl(IAudio audio, Location location)
{
if (location == Location.ActionCenter)
{
return new Controls.ActionCenter.AudioControl(audio, text);
}
else
{
return new Controls.Taskbar.AudioControl(audio, text);
}
}
internal ISystemControl CreateKeyboardLayoutControl(IKeyboard keyboard, Location location)
{
if (location == Location.ActionCenter)
{
return new Controls.ActionCenter.KeyboardLayoutControl(keyboard, text);
}
else
{
return new Controls.Taskbar.KeyboardLayoutControl(keyboard, text);
}
}
internal ISystemControl CreateNetworkControl(INetworkAdapter adapter, Location location)
{
if (location == Location.ActionCenter)
{
return new Controls.ActionCenter.NetworkControl(adapter, text);
}
else
{
return new Controls.Taskbar.NetworkControl(adapter, text);
}
}
internal INotificationControl CreateNotificationControl(INotification notification, Location location)
{
if (location == Location.ActionCenter)
{
return new Controls.ActionCenter.NotificationButton(notification);
}
else
{
return new Controls.Taskbar.NotificationButton(notification);
}
}
internal ISystemControl CreatePowerSupplyControl(IPowerSupply powerSupply, Location location)
{
if (location == Location.ActionCenter)
{
return new Controls.ActionCenter.PowerSupplyControl(powerSupply, text);
}
else
{
return new Controls.Taskbar.PowerSupplyControl(powerSupply, text);
}
}
internal INotificationControl CreateRaiseHandControl(IInvigilator invigilator, Location location, InvigilationSettings settings)
{
if (location == Location.ActionCenter)
{
return new Controls.ActionCenter.RaiseHandControl(invigilator, settings, text);
}
else
{
return new Controls.Taskbar.RaiseHandControl(invigilator, settings, text);
}
}
}
}

View File

@@ -15,8 +15,8 @@ using System.Windows.Input;
using System.Windows.Media;
using SafeExamBrowser.Core.Contracts.Resources.Icons;
using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.Proctoring.Contracts;
using SafeExamBrowser.Settings.Proctoring;
using SafeExamBrowser.Server.Contracts;
using SafeExamBrowser.Settings.Server;
using SafeExamBrowser.UserInterface.Contracts.Shell;
using SafeExamBrowser.UserInterface.Shared.Utilities;
@@ -24,16 +24,16 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls.ActionCenter
{
public partial class RaiseHandControl : UserControl, INotificationControl
{
private readonly IProctoringController controller;
private readonly ProctoringSettings settings;
private readonly IInvigilator invigilator;
private readonly InvigilationSettings settings;
private readonly IText text;
private IconResource LoweredIcon;
private IconResource RaisedIcon;
public RaiseHandControl(IProctoringController controller, ProctoringSettings settings, IText text)
public RaiseHandControl(IInvigilator invigilator, InvigilationSettings settings, IText text)
{
this.controller = controller;
this.invigilator = invigilator;
this.settings = settings;
this.text = text;
@@ -43,10 +43,11 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls.ActionCenter
private void InitializeRaiseHandControl()
{
var lastOpenedBySpacePress = false;
var originalBrush = Grid.Background;
controller.HandLowered += () => Dispatcher.Invoke(ShowLowered);
controller.HandRaised += () => Dispatcher.Invoke(ShowRaised);
invigilator.HandLowered += () => Dispatcher.Invoke(ShowLowered);
invigilator.HandRaised += () => Dispatcher.Invoke(ShowRaised);
HandButton.Click += RaiseHandButton_Click;
HandButtonText.Text = text.Get(TextKey.Notification_ProctoringRaiseHand);
@@ -55,10 +56,11 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls.ActionCenter
RaisedIcon = new XamlIconResource { Uri = new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Desktop;component/Images/Hand_Raised.xaml") };
Icon.Content = IconResourceLoader.Load(LoweredIcon);
var lastOpenedBySpacePress = false;
NotificationButton.PreviewKeyDown += (o, args) =>
{
if (args.Key == System.Windows.Input.Key.Space) // for some reason, the popup immediately closes again if opened by a Space Bar key event - as a mitigation, we record the space bar event and leave the popup open for at least 3 seconds
// For some reason, the popup immediately closes again if opened by a Space Bar key event - as a mitigation,
// we record the space bar event and leave the popup open for at least 3 seconds.
if (args.Key == Key.Space)
{
lastOpenedBySpacePress = true;
}
@@ -126,13 +128,13 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls.ActionCenter
private void ToggleHand()
{
if (controller.IsHandRaised)
if (invigilator.IsHandRaised)
{
controller.LowerHand();
invigilator.LowerHand();
}
else
{
controller.RaiseHand(Message.Text);
invigilator.RaiseHand(Message.Text);
Message.Clear();
}
}

View File

@@ -15,8 +15,8 @@ using System.Windows.Input;
using System.Windows.Media;
using SafeExamBrowser.Core.Contracts.Resources.Icons;
using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.Proctoring.Contracts;
using SafeExamBrowser.Settings.Proctoring;
using SafeExamBrowser.Server.Contracts;
using SafeExamBrowser.Settings.Server;
using SafeExamBrowser.UserInterface.Contracts.Shell;
using SafeExamBrowser.UserInterface.Shared.Utilities;
@@ -24,16 +24,16 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls.Taskbar
{
public partial class RaiseHandControl : UserControl, INotificationControl
{
private readonly IProctoringController controller;
private readonly ProctoringSettings settings;
private readonly IInvigilator invigilator;
private readonly InvigilationSettings settings;
private readonly IText text;
private IconResource LoweredIcon;
private IconResource RaisedIcon;
public RaiseHandControl(IProctoringController controller, ProctoringSettings settings, IText text)
public RaiseHandControl(IInvigilator invigilator, InvigilationSettings settings, IText text)
{
this.controller = controller;
this.invigilator = invigilator;
this.settings = settings;
this.text = text;
@@ -43,10 +43,11 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls.Taskbar
private void InitializeRaiseHandControl()
{
var lastOpenedBySpacePress = false;
var originalBrush = NotificationButton.Background;
controller.HandLowered += () => Dispatcher.Invoke(ShowLowered);
controller.HandRaised += () => Dispatcher.Invoke(ShowRaised);
invigilator.HandLowered += () => Dispatcher.Invoke(ShowLowered);
invigilator.HandRaised += () => Dispatcher.Invoke(ShowRaised);
HandButton.Click += RaiseHandButton_Click;
HandButtonText.Text = text.Get(TextKey.Notification_ProctoringRaiseHand);
@@ -55,10 +56,11 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls.Taskbar
RaisedIcon = new XamlIconResource { Uri = new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Desktop;component/Images/Hand_Raised.xaml") };
Icon.Content = IconResourceLoader.Load(LoweredIcon);
var lastOpenedBySpacePress = false;
NotificationButton.PreviewKeyDown += (o, args) =>
{
if (args.Key == System.Windows.Input.Key.Space) // for some reason, the popup immediately closes again if opened by a Space Bar key event - as a mitigation, we record the space bar event and leave the popup open for at least 3 seconds
// For some reason, the popup immediately closes again if opened by a Space Bar key event - as a mitigation,
// we record the space bar event and leave the popup open for at least 3 seconds.
if (args.Key == Key.Space)
{
lastOpenedBySpacePress = true;
}
@@ -89,7 +91,6 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls.Taskbar
Background = Brushes.LightGray;
NotificationButton.Background = Brushes.LightGray;
};
Popup.Closed += (o, args) =>
{
Background = originalBrush;
@@ -130,13 +131,13 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls.Taskbar
private void ToggleHand()
{
if (controller.IsHandRaised)
if (invigilator.IsHandRaised)
{
controller.LowerHand();
invigilator.LowerHand();
}
else
{
controller.RaiseHand(Message.Text);
invigilator.RaiseHand(Message.Text);
Message.Clear();
}
}

View File

@@ -9,18 +9,20 @@
using System.Windows;
using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.SystemComponents.Contracts;
using SafeExamBrowser.UserInterface.Contracts;
using SafeExamBrowser.UserInterface.Contracts.FileSystemDialog;
using SafeExamBrowser.UserInterface.Contracts.Windows;
using SafeExamBrowser.UserInterface.Mobile.Windows;
using SafeExamBrowser.UserInterface.Shared;
namespace SafeExamBrowser.UserInterface.Mobile
{
public class FileSystemDialogFactory : IFileSystemDialog
public class FileSystemDialogFactory : Guardable, IFileSystemDialog
{
private readonly ISystemInfo systemInfo;
private readonly IText text;
public FileSystemDialogFactory(ISystemInfo systemInfo, IText text)
public FileSystemDialogFactory(ISystemInfo systemInfo, IText text, IWindowGuard windowGuard) : base(windowGuard)
{
this.systemInfo = systemInfo;
this.text = text;
@@ -38,11 +40,20 @@ namespace SafeExamBrowser.UserInterface.Mobile
{
if (parent is Window window)
{
return window.Dispatcher.Invoke(() => new FileSystemDialog(element, operation, systemInfo, text, initialPath, message, title, parent, restrictNavigation, showElementPath).Show());
return window.Dispatcher.Invoke(() =>
{
var dialog = Guard(new FileSystemDialog(element, operation, systemInfo, text, initialPath, message, title, parent, restrictNavigation, showElementPath));
var result = dialog.Show();
return result;
});
}
else
{
return new FileSystemDialog(element, operation, systemInfo, text, initialPath, message, title, restrictNavigation: restrictNavigation, showElementPath: showElementPath).Show();
var dialog = Guard(new FileSystemDialog(element, operation, systemInfo, text, initialPath, message, title, restrictNavigation: restrictNavigation, showElementPath: showElementPath));
var result = dialog.Show();
return result;
}
}
}

View File

@@ -69,12 +69,14 @@
<Reference Include="WindowsFormsIntegration" />
</ItemGroup>
<ItemGroup>
<Compile Include="ControlFactory.cs" />
<Compile Include="Controls\ActionCenter\RaiseHandControl.xaml.cs">
<DependentUpon>RaiseHandControl.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\Taskbar\RaiseHandControl.xaml.cs">
<DependentUpon>RaiseHandControl.xaml</DependentUpon>
</Compile>
<Compile Include="WindowFactory.cs" />
<Compile Include="Windows\AboutWindow.xaml.cs">
<DependentUpon>AboutWindow.xaml</DependentUpon>
</Compile>

View File

@@ -7,8 +7,6 @@
*/
using System.Collections.Generic;
using System.Threading;
using System.Windows;
using System.Windows.Media;
using FontAwesome.WPF;
using SafeExamBrowser.Applications.Contracts;
@@ -16,10 +14,10 @@ using SafeExamBrowser.Configuration.Contracts;
using SafeExamBrowser.Core.Contracts.Notifications;
using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.Proctoring.Contracts;
using SafeExamBrowser.Server.Contracts;
using SafeExamBrowser.Server.Contracts.Data;
using SafeExamBrowser.Settings.Browser;
using SafeExamBrowser.Settings.Proctoring;
using SafeExamBrowser.Settings.Server;
using SafeExamBrowser.Settings.UserInterface;
using SafeExamBrowser.SystemComponents.Contracts.Audio;
using SafeExamBrowser.SystemComponents.Contracts.Keyboard;
@@ -31,222 +29,140 @@ using SafeExamBrowser.UserInterface.Contracts.Proctoring;
using SafeExamBrowser.UserInterface.Contracts.Shell;
using SafeExamBrowser.UserInterface.Contracts.Windows;
using SafeExamBrowser.UserInterface.Contracts.Windows.Data;
using SafeExamBrowser.UserInterface.Mobile.Windows;
using SplashScreen = SafeExamBrowser.UserInterface.Mobile.Windows.SplashScreen;
namespace SafeExamBrowser.UserInterface.Mobile
{
public class UserInterfaceFactory : IUserInterfaceFactory
{
private readonly ControlFactory controlFactory;
private readonly IText text;
private readonly WindowFactory windowFactory;
public UserInterfaceFactory(IText text)
/// <remarks>
/// The <see cref="IWindowGuard"/> is optional, as it is only used by the client application component.
/// </remarks>
public UserInterfaceFactory(IText text, IWindowGuard windowGuard = default)
{
this.controlFactory = new ControlFactory(text);
this.text = text;
this.windowFactory = new WindowFactory(text, windowGuard);
InitializeFontAwesome();
}
public IWindow CreateAboutWindow(AppConfig appConfig)
{
return new AboutWindow(appConfig, text);
return windowFactory.CreateAboutWindow(appConfig);
}
public IActionCenter CreateActionCenter()
{
return new ActionCenter();
return windowFactory.CreateActionCenter();
}
public IApplicationControl CreateApplicationControl(IApplication<IApplicationWindow> application, Location location)
{
if (location == Location.ActionCenter)
{
return new Controls.ActionCenter.ApplicationControl(application);
}
else
{
return new Controls.Taskbar.ApplicationControl(application);
}
return controlFactory.CreateApplicationControl(application, location);
}
public ISystemControl CreateAudioControl(IAudio audio, Location location)
{
if (location == Location.ActionCenter)
{
return new Controls.ActionCenter.AudioControl(audio, text);
}
else
{
return new Controls.Taskbar.AudioControl(audio, text);
}
return controlFactory.CreateAudioControl(audio, location);
}
public IBrowserWindow CreateBrowserWindow(IBrowserControl control, BrowserSettings settings, bool isMainWindow, ILogger logger)
{
return Application.Current.Dispatcher.Invoke(() => new BrowserWindow(control, settings, isMainWindow, text, logger));
return windowFactory.CreateBrowserWindow(control, settings, isMainWindow, logger);
}
public ICredentialsDialog CreateCredentialsDialog(CredentialsDialogPurpose purpose, string message, string title)
{
return Application.Current.Dispatcher.Invoke(() => new CredentialsDialog(purpose, message, title, text));
return windowFactory.CreateCredentialsDialog(purpose, message, title);
}
public IExamSelectionDialog CreateExamSelectionDialog(IEnumerable<Exam> exams)
{
return Application.Current.Dispatcher.Invoke(() => new ExamSelectionDialog(exams, text));
return windowFactory.CreateExamSelectionDialog(exams);
}
public ISystemControl CreateKeyboardLayoutControl(IKeyboard keyboard, Location location)
{
if (location == Location.ActionCenter)
{
return new Controls.ActionCenter.KeyboardLayoutControl(keyboard, text);
}
else
{
return new Controls.Taskbar.KeyboardLayoutControl(keyboard, text);
}
return controlFactory.CreateKeyboardLayoutControl(keyboard, location);
}
public ILockScreen CreateLockScreen(string message, string title, IEnumerable<LockScreenOption> options, LockScreenSettings settings)
{
return Application.Current.Dispatcher.Invoke(() => new LockScreen(message, title, settings, text, options));
return windowFactory.CreateLockScreen(message, title, options, settings);
}
public IWindow CreateLogWindow(ILogger logger)
{
var window = default(LogWindow);
var windowReadyEvent = new AutoResetEvent(false);
var windowThread = new Thread(() =>
{
window = new LogWindow(logger, text);
window.Closed += (o, args) => window.Dispatcher.InvokeShutdown();
window.Show();
windowReadyEvent.Set();
System.Windows.Threading.Dispatcher.Run();
});
windowThread.SetApartmentState(ApartmentState.STA);
windowThread.IsBackground = true;
windowThread.Start();
windowReadyEvent.WaitOne();
return window;
return windowFactory.CreateLogWindow(logger);
}
public ISystemControl CreateNetworkControl(INetworkAdapter adapter, Location location)
{
if (location == Location.ActionCenter)
{
return new Controls.ActionCenter.NetworkControl(adapter, text);
}
else
{
return new Controls.Taskbar.NetworkControl(adapter, text);
}
return controlFactory.CreateNetworkControl(adapter, location);
}
public INotificationControl CreateNotificationControl(INotification notification, Location location)
{
if (location == Location.ActionCenter)
{
return new Controls.ActionCenter.NotificationButton(notification);
}
else
{
return new Controls.Taskbar.NotificationButton(notification);
}
return controlFactory.CreateNotificationControl(notification, location);
}
public IPasswordDialog CreatePasswordDialog(string message, string title)
{
return Application.Current.Dispatcher.Invoke(() => new PasswordDialog(message, title, text));
return windowFactory.CreatePasswordDialog(message, title);
}
public IPasswordDialog CreatePasswordDialog(TextKey message, TextKey title)
{
return Application.Current.Dispatcher.Invoke(() => new PasswordDialog(text.Get(message), text.Get(title), text));
return windowFactory.CreatePasswordDialog(text.Get(message), text.Get(title));
}
public ISystemControl CreatePowerSupplyControl(IPowerSupply powerSupply, Location location)
{
if (location == Location.ActionCenter)
{
return new Controls.ActionCenter.PowerSupplyControl(powerSupply, text);
}
else
{
return new Controls.Taskbar.PowerSupplyControl(powerSupply, text);
}
return controlFactory.CreatePowerSupplyControl(powerSupply, location);
}
public IProctoringFinalizationDialog CreateProctoringFinalizationDialog()
public IProctoringFinalizationDialog CreateProctoringFinalizationDialog(bool requiresPassword)
{
return Application.Current.Dispatcher.Invoke(() => new ProctoringFinalizationDialog(text));
return windowFactory.CreateProctoringFinalizationDialog(requiresPassword);
}
public IProctoringWindow CreateProctoringWindow(IProctoringControl control)
{
return Application.Current.Dispatcher.Invoke(() => new ProctoringWindow(control));
return windowFactory.CreateProctoringWindow(control);
}
public INotificationControl CreateRaiseHandControl(IProctoringController controller, Location location, ProctoringSettings settings)
public INotificationControl CreateRaiseHandControl(IInvigilator invigilator, Location location, InvigilationSettings settings)
{
if (location == Location.ActionCenter)
{
return new Controls.ActionCenter.RaiseHandControl(controller, settings, text);
}
else
{
return new Controls.Taskbar.RaiseHandControl(controller, settings, text);
}
return controlFactory.CreateRaiseHandControl(invigilator, location, settings);
}
public IRuntimeWindow CreateRuntimeWindow(AppConfig appConfig)
{
return Application.Current.Dispatcher.Invoke(() => new RuntimeWindow(appConfig, text));
return windowFactory.CreateRuntimeWindow(appConfig);
}
public IServerFailureDialog CreateServerFailureDialog(string info, bool showFallback)
{
return Application.Current.Dispatcher.Invoke(() => new ServerFailureDialog(info, showFallback, text));
return windowFactory.CreateServerFailureDialog(info, showFallback);
}
public ISplashScreen CreateSplashScreen(AppConfig appConfig = null)
{
var window = default(SplashScreen);
var windowReadyEvent = new AutoResetEvent(false);
var windowThread = new Thread(() =>
{
window = new SplashScreen(text, appConfig);
window.Closed += (o, args) => window.Dispatcher.InvokeShutdown();
window.Show();
windowReadyEvent.Set();
System.Windows.Threading.Dispatcher.Run();
});
windowThread.SetApartmentState(ApartmentState.STA);
windowThread.IsBackground = true;
windowThread.Start();
windowReadyEvent.WaitOne();
return window;
return windowFactory.CreateSplashScreen(appConfig);
}
public ITaskbar CreateTaskbar(ILogger logger)
{
return new Taskbar(logger);
return windowFactory.CreateTaskbar(logger);
}
public ITaskview CreateTaskview()
{
return new Taskview();
return windowFactory.CreateTaskview();
}
private void InitializeFontAwesome()

View File

@@ -0,0 +1,157 @@
/*
* Copyright (c) 2025 ETH Zürich, IT Services
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
using System.Collections.Generic;
using System.Threading;
using System.Windows;
using SafeExamBrowser.Configuration.Contracts;
using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.Server.Contracts.Data;
using SafeExamBrowser.Settings.Browser;
using SafeExamBrowser.Settings.UserInterface;
using SafeExamBrowser.UserInterface.Contracts;
using SafeExamBrowser.UserInterface.Contracts.Browser;
using SafeExamBrowser.UserInterface.Contracts.Proctoring;
using SafeExamBrowser.UserInterface.Contracts.Shell;
using SafeExamBrowser.UserInterface.Contracts.Windows;
using SafeExamBrowser.UserInterface.Contracts.Windows.Data;
using SafeExamBrowser.UserInterface.Mobile.Windows;
using SafeExamBrowser.UserInterface.Shared;
using SplashScreen = SafeExamBrowser.UserInterface.Mobile.Windows.SplashScreen;
namespace SafeExamBrowser.UserInterface.Mobile
{
internal class WindowFactory : Guardable
{
private readonly IText text;
internal WindowFactory(IText text, IWindowGuard windowGuard = default) : base(windowGuard)
{
this.text = text;
}
internal IWindow CreateAboutWindow(AppConfig appConfig)
{
return Guard(new AboutWindow(appConfig, text));
}
internal IActionCenter CreateActionCenter()
{
return Guard(new ActionCenter());
}
internal IBrowserWindow CreateBrowserWindow(IBrowserControl control, BrowserSettings settings, bool isMainWindow, ILogger logger)
{
return Application.Current.Dispatcher.Invoke(() => Guard(new BrowserWindow(control, settings, isMainWindow, text, logger)));
}
internal ICredentialsDialog CreateCredentialsDialog(CredentialsDialogPurpose purpose, string message, string title)
{
return Application.Current.Dispatcher.Invoke(() => Guard(new CredentialsDialog(purpose, message, title, text)));
}
internal IExamSelectionDialog CreateExamSelectionDialog(IEnumerable<Exam> exams)
{
return Application.Current.Dispatcher.Invoke(() => Guard(new ExamSelectionDialog(exams, text)));
}
internal ILockScreen CreateLockScreen(string message, string title, IEnumerable<LockScreenOption> options, LockScreenSettings settings)
{
return Application.Current.Dispatcher.Invoke(() => Guard(new LockScreen(message, title, settings, text, options)));
}
internal IWindow CreateLogWindow(ILogger logger)
{
var window = default(LogWindow);
var windowReadyEvent = new AutoResetEvent(false);
var windowThread = new Thread(() =>
{
window = Guard(new LogWindow(logger, text));
window.Closed += (o, args) => window.Dispatcher.InvokeShutdown();
window.Show();
windowReadyEvent.Set();
System.Windows.Threading.Dispatcher.Run();
});
windowThread.SetApartmentState(ApartmentState.STA);
windowThread.IsBackground = true;
windowThread.Start();
windowReadyEvent.WaitOne();
return window;
}
internal IPasswordDialog CreatePasswordDialog(string message, string title)
{
return Application.Current.Dispatcher.Invoke(() => Guard(new PasswordDialog(message, title, text)));
}
internal IPasswordDialog CreatePasswordDialog(TextKey message, TextKey title)
{
return Application.Current.Dispatcher.Invoke(() => Guard(new PasswordDialog(text.Get(message), text.Get(title), text)));
}
internal IProctoringFinalizationDialog CreateProctoringFinalizationDialog(bool requiresPassword)
{
return Application.Current.Dispatcher.Invoke(() => Guard(new ProctoringFinalizationDialog(requiresPassword, text)));
}
internal IProctoringWindow CreateProctoringWindow(IProctoringControl control)
{
return Application.Current.Dispatcher.Invoke(() => Guard(new ProctoringWindow(control)));
}
internal IRuntimeWindow CreateRuntimeWindow(AppConfig appConfig)
{
return Application.Current.Dispatcher.Invoke(() => Guard(new RuntimeWindow(appConfig, text)));
}
internal IServerFailureDialog CreateServerFailureDialog(string info, bool showFallback)
{
return Application.Current.Dispatcher.Invoke(() => Guard(new ServerFailureDialog(info, showFallback, text)));
}
internal ISplashScreen CreateSplashScreen(AppConfig appConfig = null)
{
var window = default(SplashScreen);
var windowReadyEvent = new AutoResetEvent(false);
var windowThread = new Thread(() =>
{
window = Guard(new SplashScreen(text, appConfig));
window.Closed += (o, args) => window.Dispatcher.InvokeShutdown();
window.Show();
windowReadyEvent.Set();
System.Windows.Threading.Dispatcher.Run();
});
windowThread.SetApartmentState(ApartmentState.STA);
windowThread.IsBackground = true;
windowThread.Start();
windowReadyEvent.WaitOne();
return window;
}
internal ITaskbar CreateTaskbar(ILogger logger)
{
return Guard(new Taskbar(logger));
}
internal ITaskview CreateTaskview()
{
return Guard(new Taskview());
}
}
}

View File

@@ -47,10 +47,14 @@
</WrapPanel>
</Grid>
</Grid>
<Grid Grid.Row="2" Name="ButtonPanel" Background="{StaticResource BackgroundBrush}">
<StackPanel Grid.Row="2" Name="ButtonPanel" Background="{StaticResource BackgroundBrush}" Orientation="Vertical">
<StackPanel Grid.Row="1" Name="PasswordPanel" Margin="50,50,50,0" Orientation="Vertical">
<TextBlock Name="PasswordLabel" Margin="0,0,0,10" />
<PasswordBox Name="Password" Height="35" VerticalContentAlignment="Center" />
</StackPanel>
<WrapPanel Orientation="Horizontal" Margin="50,25" HorizontalAlignment="Right" VerticalAlignment="Center">
<Button Name="Button" Cursor="Hand" Padding="10,5" MinWidth="75" />
</WrapPanel>
</Grid>
</StackPanel>
</Grid>
</Window>

View File

@@ -12,24 +12,51 @@ using System.Windows.Input;
using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.Proctoring.Contracts.Events;
using SafeExamBrowser.UserInterface.Contracts.Proctoring;
using SafeExamBrowser.UserInterface.Contracts.Proctoring.Events;
using SafeExamBrowser.UserInterface.Contracts.Windows;
using SafeExamBrowser.UserInterface.Contracts.Windows.Events;
namespace SafeExamBrowser.UserInterface.Mobile.Windows
{
public partial class ProctoringFinalizationDialog : Window, IProctoringFinalizationDialog
{
private readonly bool requiresPassword;
private readonly IText text;
private bool cancellationRequested;
private WindowClosedEventHandler closed;
private WindowClosingEventHandler closing;
private bool initialized;
public ProctoringFinalizationDialog(IText text)
public string QuitPassword => Password.Password;
public event CancellationRequestedEventHandler CancellationRequested;
event WindowClosedEventHandler IWindow.Closed
{
add { closed += value; }
remove { closed -= value; }
}
event WindowClosingEventHandler IWindow.Closing
{
add { closing += value; }
remove { closing -= value; }
}
public ProctoringFinalizationDialog(bool requiresPassword, IText text)
{
this.requiresPassword = requiresPassword;
this.text = text;
InitializeComponent();
InitializeDialog();
}
public void BringToForeground()
{
Dispatcher.Invoke(Activate);
}
public new void Show()
{
Dispatcher.Invoke(() =>
@@ -66,8 +93,28 @@ namespace SafeExamBrowser.UserInterface.Mobile.Windows
Width = SystemParameters.PrimaryScreenWidth;
}
private void InitializeCancellation()
{
Button.Click += Button_Click;
Button.Content = text.Get(TextKey.ProctoringFinalizationDialog_Abort);
if (requiresPassword)
{
PasswordPanel.Visibility = Visibility.Visible;
PasswordLabel.Text = text.Get(TextKey.ProctoringFinalizationDialog_PasswordMessage);
Password.KeyDown += Password_KeyDown;
Password.Focus();
Height += PasswordPanel.ActualHeight + PasswordPanel.Margin.Top + PasswordPanel.Margin.Bottom;
}
initialized = true;
}
private void InitializeDialog()
{
Closed += (o, args) => closed?.Invoke();
Closing += (o, args) => closing?.Invoke();
Title = text.Get(TextKey.ProctoringFinalizationDialog_Title);
InitializeBounds();
@@ -77,9 +124,11 @@ namespace SafeExamBrowser.UserInterface.Mobile.Windows
private void ShowFailure(RemainingWorkUpdatedEventArgs status)
{
ButtonPanel.Visibility = Visibility.Visible;
Button.Click -= Button_Click;
Button.Click += (o, args) => Close();
Button.Content = text.Get(TextKey.ProctoringFinalizationDialog_Confirm);
ButtonPanel.Visibility = Visibility.Visible;
Button.Focus();
// TODO: Revert once cache handling has been specified and changed!
CachePath.Text = status.CachePath ?? "-";
@@ -87,7 +136,9 @@ namespace SafeExamBrowser.UserInterface.Mobile.Windows
Cursor = Cursors.Arrow;
FailurePanel.Visibility = Visibility.Visible;
Height -= PasswordPanel.IsVisible ? PasswordPanel.ActualHeight + PasswordPanel.Margin.Top + PasswordPanel.Margin.Bottom : 0;
Message.Text = text.Get(TextKey.ProctoringFinalizationDialog_FailureMessage);
PasswordPanel.Visibility = Visibility.Collapsed;
ProgressPanel.Visibility = Visibility.Collapsed;
}
@@ -101,9 +152,11 @@ namespace SafeExamBrowser.UserInterface.Mobile.Windows
if (status.AllowCancellation && !initialized)
{
Button.Click += (o, args) => cancellationRequested = true;
Button.Content = text.Get(TextKey.ProctoringFinalizationDialog_Abort);
initialized = true;
InitializeCancellation();
}
else if (!status.AllowCancellation)
{
PasswordPanel.Visibility = Visibility.Collapsed;
}
if (status.IsWaiting)
@@ -114,8 +167,6 @@ namespace SafeExamBrowser.UserInterface.Mobile.Windows
{
UpdateProgress(status);
}
status.CancellationRequested = cancellationRequested;
}
private void UpdateProgress(RemainingWorkUpdatedEventArgs status)
@@ -165,5 +216,18 @@ namespace SafeExamBrowser.UserInterface.Mobile.Windows
Dispatcher.InvokeAsync(InitializeBounds);
}
}
private void Button_Click(object sender, RoutedEventArgs e)
{
CancellationRequested?.Invoke();
}
private void Password_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter)
{
CancellationRequested?.Invoke();
}
}
}
}