SEBWIN-219: Continued implementing kiosk operation and realized that further changes to startup/runtime/shutdown-mechanism are necessary...
This commit is contained in:
parent
b22093e6a2
commit
18b8f66300
30 changed files with 505 additions and 82 deletions
|
@ -15,6 +15,7 @@ namespace SafeExamBrowser.Configuration.Settings
|
|||
internal class Settings : ISettings
|
||||
{
|
||||
public ConfigurationMode ConfigurationMode { get; set; }
|
||||
public KioskMode KioskMode { get; set; }
|
||||
public ServicePolicy ServicePolicy { get; set; }
|
||||
|
||||
public IBrowserSettings Browser { get; set; }
|
||||
|
|
|
@ -14,5 +14,10 @@ namespace SafeExamBrowser.Contracts.Behaviour
|
|||
/// Reverts any changes performed during the startup or runtime and releases all used resources.
|
||||
/// </summary>
|
||||
void FinalizeApplication();
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new session and starts performing the runtime logic / event handling.
|
||||
/// </summary>
|
||||
void StartSession();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,13 +25,18 @@ namespace SafeExamBrowser.Contracts.Configuration.Settings
|
|||
/// </summary>
|
||||
IKeyboardSettings Keyboard { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The kiosk mode which determines how the computer is locked down.
|
||||
/// </summary>
|
||||
KioskMode KioskMode { get; }
|
||||
|
||||
/// <summary>
|
||||
/// All mouse-related settings.
|
||||
/// </summary>
|
||||
IMouseSettings Mouse { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The active service policy.
|
||||
/// The active policy for the service component.
|
||||
/// </summary>
|
||||
ServicePolicy ServicePolicy { get; }
|
||||
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Copyright (c) 2018 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.Contracts.Configuration.Settings
|
||||
{
|
||||
public enum KioskMode
|
||||
{
|
||||
/// <summary>
|
||||
/// No kiosk mode - should only be used for testing / debugging.
|
||||
/// </summary>
|
||||
None,
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new desktop and runs the client application on it, without modifying the default desktop.
|
||||
/// </summary>
|
||||
CreateNewDesktop,
|
||||
|
||||
/// <summary>
|
||||
/// Terminates the Windows explorer shell and runs the client application on the default desktop.
|
||||
/// </summary>
|
||||
DisableExplorerShell
|
||||
}
|
||||
}
|
|
@ -26,16 +26,20 @@ namespace SafeExamBrowser.Contracts.I18n
|
|||
Notification_AboutTooltip,
|
||||
Notification_LogTooltip,
|
||||
RuntimeWindow_ApplicationRunning,
|
||||
RuntimeWindow_StartSession,
|
||||
RuntimeWindow_StopSession,
|
||||
SplashScreen_CloseServiceConnection,
|
||||
SplashScreen_EmptyClipboard,
|
||||
SplashScreen_InitializeBrowser,
|
||||
SplashScreen_InitializeConfiguration,
|
||||
SplashScreen_InitializeKioskMode,
|
||||
SplashScreen_InitializeProcessMonitoring,
|
||||
SplashScreen_InitializeServiceConnection,
|
||||
SplashScreen_InitializeTaskbar,
|
||||
SplashScreen_InitializeWindowMonitoring,
|
||||
SplashScreen_InitializeWorkingArea,
|
||||
SplashScreen_RestoreWorkingArea,
|
||||
SplashScreen_RevertKioskMode,
|
||||
SplashScreen_ShutdownProcedure,
|
||||
SplashScreen_StartEventHandling,
|
||||
SplashScreen_StartKeyboardInterception,
|
||||
|
|
|
@ -81,6 +81,7 @@
|
|||
<Compile Include="Behaviour\IStartupController.cs" />
|
||||
<Compile Include="Configuration\Settings\ISettingsRepository.cs" />
|
||||
<Compile Include="Configuration\Settings\ITaskbarSettings.cs" />
|
||||
<Compile Include="Configuration\Settings\KioskMode.cs" />
|
||||
<Compile Include="Configuration\Settings\ServicePolicy.cs" />
|
||||
<Compile Include="I18n\IText.cs" />
|
||||
<Compile Include="I18n\TextKey.cs" />
|
||||
|
|
|
@ -7,15 +7,16 @@
|
|||
*/
|
||||
|
||||
using SafeExamBrowser.Contracts.I18n;
|
||||
using SafeExamBrowser.Contracts.Logging;
|
||||
|
||||
namespace SafeExamBrowser.Contracts.UserInterface
|
||||
{
|
||||
public interface IRuntimeWindow : IWindow
|
||||
public interface IRuntimeWindow : ILogObserver, IWindow
|
||||
{
|
||||
/// <summary>
|
||||
/// Updates the status text of the runtime window. If the busy flag is set,
|
||||
/// the window will show an animation to indicate a long-running operation.
|
||||
/// </summary>
|
||||
void UpdateStatus(TextKey key);
|
||||
void UpdateStatus(TextKey key, bool showBusyIndication = false);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,18 +10,8 @@ using SafeExamBrowser.Contracts.I18n;
|
|||
|
||||
namespace SafeExamBrowser.Contracts.UserInterface
|
||||
{
|
||||
public interface ISplashScreen
|
||||
public interface ISplashScreen : IWindow
|
||||
{
|
||||
/// <summary>
|
||||
/// Closes the splash screen on its own thread.
|
||||
/// </summary>
|
||||
void InvokeClose();
|
||||
|
||||
/// <summary>
|
||||
/// Shows the splash screen on its own thread.
|
||||
/// </summary>
|
||||
void InvokeShow();
|
||||
|
||||
/// <summary>
|
||||
/// Updates the progress bar of the splash screen according to the specified amount.
|
||||
/// </summary>
|
||||
|
|
|
@ -51,6 +51,12 @@ namespace SafeExamBrowser.Contracts.UserInterface
|
|||
/// </summary>
|
||||
ISystemPowerSupplyControl CreatePowerSupplyControl();
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new runtime window which runs on its own thread.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
IRuntimeWindow CreateRuntimeWindow(IRuntimeInfo runtimeInfo, IText text);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new splash screen which runs on its own thread.
|
||||
/// </summary>
|
||||
|
|
|
@ -27,6 +27,11 @@ namespace SafeExamBrowser.Contracts.UserInterface
|
|||
/// </summary>
|
||||
void Close();
|
||||
|
||||
/// <summary>
|
||||
/// Hides the window.
|
||||
/// </summary>
|
||||
void Hide();
|
||||
|
||||
/// <summary>
|
||||
/// Shows the window to the user.
|
||||
/// </summary>
|
||||
|
|
|
@ -72,7 +72,7 @@ namespace SafeExamBrowser.Core.Behaviour
|
|||
splashScreen = uiFactory.CreateSplashScreen(runtimeInfo, text);
|
||||
splashScreen.SetIndeterminate();
|
||||
splashScreen.UpdateText(TextKey.SplashScreen_ShutdownProcedure);
|
||||
splashScreen.InvokeShow();
|
||||
splashScreen.Show();
|
||||
}
|
||||
|
||||
private void LogAndShowException(Exception e)
|
||||
|
@ -92,7 +92,7 @@ namespace SafeExamBrowser.Core.Behaviour
|
|||
logger.Info("--- Shutdown procedure failed! ---");
|
||||
}
|
||||
|
||||
splashScreen?.InvokeClose();
|
||||
splashScreen?.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -117,7 +117,7 @@ namespace SafeExamBrowser.Core.Behaviour
|
|||
splashScreen = uiFactory.CreateSplashScreen(runtimeInfo, text);
|
||||
splashScreen.SetMaxProgress(operationCount);
|
||||
splashScreen.UpdateText(TextKey.SplashScreen_StartupProcedure);
|
||||
splashScreen.InvokeShow();
|
||||
splashScreen.Show();
|
||||
}
|
||||
|
||||
private void LogAndShowException(Exception e)
|
||||
|
@ -139,7 +139,7 @@ namespace SafeExamBrowser.Core.Behaviour
|
|||
logger.Info("--- Startup procedure aborted! ---");
|
||||
}
|
||||
|
||||
splashScreen?.InvokeClose();
|
||||
splashScreen?.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,6 +33,12 @@
|
|||
<Entry key="RuntimeWindow_ApplicationRunning">
|
||||
The application is running.
|
||||
</Entry>
|
||||
<Entry key="RuntimeWindow_StartSession">
|
||||
Starting new session
|
||||
</Entry>
|
||||
<Entry key="RuntimeWindow_StopSession">
|
||||
Stopping current session
|
||||
</Entry>
|
||||
<Entry key="SplashScreen_CloseServiceConnection">
|
||||
Closing service connection
|
||||
</Entry>
|
||||
|
@ -45,6 +51,9 @@
|
|||
<Entry key="SplashScreen_InitializeConfiguration">
|
||||
Initializing application configuration
|
||||
</Entry>
|
||||
<Entry key="SplashScreen_InitializeKioskMode">
|
||||
Initializing kiosk mode
|
||||
</Entry>
|
||||
<Entry key="SplashScreen_InitializeProcessMonitoring">
|
||||
Initializing process monitoring
|
||||
</Entry>
|
||||
|
@ -63,6 +72,9 @@
|
|||
<Entry key="SplashScreen_RestoreWorkingArea">
|
||||
Restoring working area
|
||||
</Entry>
|
||||
<Entry key="SplashScreen_RevertKioskMode">
|
||||
Reverting kiosk mode
|
||||
</Entry>
|
||||
<Entry key="SplashScreen_ShutdownProcedure">
|
||||
Initiating shutdown procedure
|
||||
</Entry>
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
*/
|
||||
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Threading;
|
||||
using System.Windows;
|
||||
|
||||
|
@ -56,6 +55,8 @@ namespace SafeExamBrowser.Runtime
|
|||
{
|
||||
base.OnStartup(e);
|
||||
|
||||
ShutdownMode = ShutdownMode.OnExplicitShutdown;
|
||||
|
||||
instances.BuildObjectGraph();
|
||||
instances.LogStartupInformation();
|
||||
|
||||
|
@ -63,9 +64,7 @@ namespace SafeExamBrowser.Runtime
|
|||
|
||||
if (success)
|
||||
{
|
||||
MainWindow = instances.RuntimeWindow;
|
||||
MainWindow.Closing += MainWindow_Closing;
|
||||
MainWindow.Show();
|
||||
instances.RuntimeController.StartSession();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -75,15 +74,10 @@ namespace SafeExamBrowser.Runtime
|
|||
|
||||
protected override void OnExit(ExitEventArgs e)
|
||||
{
|
||||
instances.RuntimeController.FinalizeApplication();
|
||||
instances.LogShutdownInformation();
|
||||
|
||||
base.OnExit(e);
|
||||
}
|
||||
|
||||
private void MainWindow_Closing(object sender, CancelEventArgs e)
|
||||
{
|
||||
MainWindow.Hide();
|
||||
instances.RuntimeController.FinalizeApplication();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,25 +6,79 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using SafeExamBrowser.Contracts.Behaviour;
|
||||
using SafeExamBrowser.Contracts.Configuration.Settings;
|
||||
using SafeExamBrowser.Contracts.I18n;
|
||||
using SafeExamBrowser.Contracts.Logging;
|
||||
using SafeExamBrowser.Contracts.UserInterface;
|
||||
|
||||
namespace SafeExamBrowser.Runtime.Behaviour.Operations
|
||||
{
|
||||
internal class KioskModeOperation : IOperation
|
||||
{
|
||||
private ILogger logger;
|
||||
private ISettingsRepository settingsRepository;
|
||||
private KioskMode kioskMode;
|
||||
|
||||
public bool AbortStartup { get; private set; }
|
||||
public ISplashScreen SplashScreen { private get; set; }
|
||||
|
||||
public KioskModeOperation(ILogger logger, ISettingsRepository settingsRepository)
|
||||
{
|
||||
this.logger = logger;
|
||||
this.settingsRepository = settingsRepository;
|
||||
}
|
||||
|
||||
public void Perform()
|
||||
{
|
||||
// TODO
|
||||
kioskMode = settingsRepository.Current.KioskMode;
|
||||
|
||||
logger.Info($"Initializing kiosk mode '{kioskMode}'...");
|
||||
SplashScreen.UpdateText(TextKey.SplashScreen_InitializeKioskMode);
|
||||
|
||||
if (kioskMode == KioskMode.CreateNewDesktop)
|
||||
{
|
||||
CreateNewDesktop();
|
||||
}
|
||||
else
|
||||
{
|
||||
DisableExplorerShell();
|
||||
}
|
||||
}
|
||||
|
||||
public void Revert()
|
||||
{
|
||||
// TODO
|
||||
logger.Info($"Reverting kiosk mode '{kioskMode}'...");
|
||||
SplashScreen.UpdateText(TextKey.SplashScreen_RevertKioskMode);
|
||||
|
||||
if (kioskMode == KioskMode.CreateNewDesktop)
|
||||
{
|
||||
CloseNewDesktop();
|
||||
}
|
||||
else
|
||||
{
|
||||
RestartExplorerShell();
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateNewDesktop()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
private void CloseNewDesktop()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
private void DisableExplorerShell()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
private void RestartExplorerShell()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,10 +6,12 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using SafeExamBrowser.Contracts.Behaviour;
|
||||
using SafeExamBrowser.Contracts.Communication;
|
||||
using SafeExamBrowser.Contracts.Configuration;
|
||||
using SafeExamBrowser.Contracts.Configuration.Settings;
|
||||
using SafeExamBrowser.Contracts.I18n;
|
||||
using SafeExamBrowser.Contracts.Logging;
|
||||
|
@ -19,66 +21,107 @@ namespace SafeExamBrowser.Runtime.Behaviour
|
|||
{
|
||||
internal class RuntimeController : IRuntimeController
|
||||
{
|
||||
private ICommunication serviceProxy;
|
||||
private Queue<IOperation> operations;
|
||||
private ILogger logger;
|
||||
private IRuntimeInfo runtimeInfo;
|
||||
private IRuntimeWindow runtimeWindow;
|
||||
private IServiceProxy serviceProxy;
|
||||
private ISettingsRepository settingsRepository;
|
||||
private IShutdownController shutdownController;
|
||||
private IStartupController startupController;
|
||||
|
||||
public ISettings Settings { private get; set; }
|
||||
private Action terminationCallback;
|
||||
private IText text;
|
||||
private IUserInterfaceFactory uiFactory;
|
||||
|
||||
public RuntimeController(
|
||||
ICommunication serviceProxy,
|
||||
ILogger logger,
|
||||
IRuntimeWindow runtimeWindow,
|
||||
IRuntimeInfo runtimeInfo,
|
||||
IServiceProxy serviceProxy,
|
||||
ISettingsRepository settingsRepository,
|
||||
IShutdownController shutdownController,
|
||||
IStartupController startupController)
|
||||
IStartupController startupController,
|
||||
Action terminationCallback,
|
||||
IText text,
|
||||
IUserInterfaceFactory uiFactory)
|
||||
{
|
||||
this.serviceProxy = serviceProxy;
|
||||
this.logger = logger;
|
||||
this.runtimeWindow = runtimeWindow;
|
||||
this.runtimeInfo = runtimeInfo;
|
||||
this.serviceProxy = serviceProxy;
|
||||
this.settingsRepository = settingsRepository;
|
||||
this.shutdownController = shutdownController;
|
||||
this.startupController = startupController;
|
||||
this.terminationCallback = terminationCallback;
|
||||
this.text = text;
|
||||
this.uiFactory = uiFactory;
|
||||
|
||||
operations = new Queue<IOperation>();
|
||||
}
|
||||
|
||||
public bool TryInitializeApplication(Queue<IOperation> operations)
|
||||
{
|
||||
operations = new Queue<IOperation>(operations);
|
||||
|
||||
var success = startupController.TryInitializeApplication(operations);
|
||||
|
||||
runtimeWindow = uiFactory.CreateRuntimeWindow(runtimeInfo, text);
|
||||
|
||||
if (success)
|
||||
{
|
||||
Start();
|
||||
this.operations = new Queue<IOperation>(operations);
|
||||
logger.Subscribe(runtimeWindow);
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
public void StartSession()
|
||||
{
|
||||
runtimeWindow.Show();
|
||||
|
||||
logger.Info("Starting new session...");
|
||||
runtimeWindow.UpdateStatus(TextKey.RuntimeWindow_StartSession, true);
|
||||
|
||||
// TODO:
|
||||
// - Initialize configuration
|
||||
// - Initialize kiosk mode
|
||||
// - Initialize session data
|
||||
// - Start runtime communication host
|
||||
// - Create and connect to client
|
||||
// - Initialize session with service
|
||||
// - Verify session integrity and start event handling
|
||||
System.Threading.Thread.Sleep(10000);
|
||||
|
||||
runtimeWindow.UpdateStatus(TextKey.RuntimeWindow_ApplicationRunning);
|
||||
|
||||
if (settingsRepository.Current.KioskMode == KioskMode.DisableExplorerShell)
|
||||
{
|
||||
runtimeWindow.Hide();
|
||||
}
|
||||
}
|
||||
|
||||
public void FinalizeApplication()
|
||||
{
|
||||
Stop();
|
||||
StopSession();
|
||||
|
||||
// TODO:
|
||||
// - Disconnect from service
|
||||
// - Terminate runtime communication host
|
||||
// - Revert kiosk mode (or do that when stopping session?)
|
||||
|
||||
logger.Unsubscribe(runtimeWindow);
|
||||
runtimeWindow.Close();
|
||||
shutdownController.FinalizeApplication(new Queue<IOperation>(operations.Reverse()));
|
||||
}
|
||||
|
||||
private void Start()
|
||||
private void StopSession()
|
||||
{
|
||||
logger.Info("Starting event handling...");
|
||||
// TODO SplashScreen.UpdateText(TextKey.SplashScreen_StartEventHandling);
|
||||
logger.Info("Stopping current session...");
|
||||
runtimeWindow.Show();
|
||||
runtimeWindow.BringToForeground();
|
||||
runtimeWindow.UpdateStatus(TextKey.RuntimeWindow_StopSession, true);
|
||||
|
||||
runtimeWindow.UpdateStatus(TextKey.RuntimeWindow_ApplicationRunning);
|
||||
}
|
||||
|
||||
private void Stop()
|
||||
{
|
||||
logger.Info("Stopping event handling...");
|
||||
// TODO SplashScreen.UpdateText(TextKey.SplashScreen_StopEventHandling);
|
||||
// TODO:
|
||||
// - Terminate client (or does it terminate itself?)
|
||||
// - Finalize session with service
|
||||
// - Stop event handling and close session
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Windows;
|
||||
using SafeExamBrowser.Configuration;
|
||||
using SafeExamBrowser.Configuration.Settings;
|
||||
using SafeExamBrowser.Contracts.Behaviour;
|
||||
|
@ -35,7 +36,6 @@ namespace SafeExamBrowser.Runtime
|
|||
|
||||
internal IRuntimeController RuntimeController { get; private set; }
|
||||
internal Queue<IOperation> StartupOperations { get; private set; }
|
||||
internal RuntimeWindow RuntimeWindow { get; private set; }
|
||||
|
||||
internal void BuildObjectGraph()
|
||||
{
|
||||
|
@ -56,16 +56,13 @@ namespace SafeExamBrowser.Runtime
|
|||
var shutdownController = new ShutdownController(logger, runtimeInfo, text, uiFactory);
|
||||
var startupController = new StartupController(logger, runtimeInfo, systemInfo, text, uiFactory);
|
||||
|
||||
RuntimeWindow = new RuntimeWindow(new DefaultLogFormatter(), runtimeInfo, text);
|
||||
RuntimeController = new RuntimeController(serviceProxy, new ModuleLogger(logger, typeof(RuntimeController)), RuntimeWindow, settingsRepository, shutdownController, startupController);
|
||||
|
||||
logger.Subscribe(RuntimeWindow);
|
||||
RuntimeController = new RuntimeController(new ModuleLogger(logger, typeof(RuntimeController)), runtimeInfo, serviceProxy, settingsRepository, shutdownController, startupController, Application.Current.Shutdown, text, uiFactory);
|
||||
|
||||
StartupOperations = new Queue<IOperation>();
|
||||
StartupOperations.Enqueue(new I18nOperation(logger, text));
|
||||
StartupOperations.Enqueue(new ConfigurationOperation(logger, runtimeInfo, settingsRepository, text, uiFactory, args));
|
||||
StartupOperations.Enqueue(new ServiceOperation(logger, serviceProxy, settingsRepository, text));
|
||||
StartupOperations.Enqueue(new KioskModeOperation());
|
||||
StartupOperations.Enqueue(new KioskModeOperation(logger, settingsRepository));
|
||||
}
|
||||
|
||||
internal void LogStartupInformation()
|
||||
|
|
|
@ -55,12 +55,30 @@ namespace SafeExamBrowser.UserInterface.Classic
|
|||
|
||||
public void BringToForeground()
|
||||
{
|
||||
if (WindowState == WindowState.Minimized)
|
||||
Dispatcher.Invoke(() =>
|
||||
{
|
||||
WindowState = WindowState.Normal;
|
||||
}
|
||||
if (WindowState == WindowState.Minimized)
|
||||
{
|
||||
WindowState = WindowState.Normal;
|
||||
}
|
||||
|
||||
Activate();
|
||||
Activate();
|
||||
});
|
||||
}
|
||||
|
||||
public new void Close()
|
||||
{
|
||||
Dispatcher.Invoke(base.Close);
|
||||
}
|
||||
|
||||
public new void Hide()
|
||||
{
|
||||
Dispatcher.Invoke(base.Hide);
|
||||
}
|
||||
|
||||
public new void Show()
|
||||
{
|
||||
Dispatcher.Invoke(base.Show);
|
||||
}
|
||||
|
||||
public void UpdateAddress(string url)
|
||||
|
|
|
@ -47,6 +47,11 @@ namespace SafeExamBrowser.UserInterface.Classic
|
|||
Dispatcher.Invoke(base.Close);
|
||||
}
|
||||
|
||||
public new void Hide()
|
||||
{
|
||||
Dispatcher.Invoke(base.Hide);
|
||||
}
|
||||
|
||||
public new void Show()
|
||||
{
|
||||
Dispatcher.Invoke(base.Show);
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
mc:Ignorable="d" Background="White" Foreground="White" Height="500" Width="750" WindowStyle="None" WindowStartupLocation="CenterScreen"
|
||||
Icon="./Images/SafeExamBrowser.ico" ResizeMode="NoResize" Title="Safe Exam Browser" Topmost="True">
|
||||
<Grid>
|
||||
<Border Panel.ZIndex="10" BorderBrush="DodgerBlue" BorderThickness="5">
|
||||
<Border x:Name="AnimatedBorder" Panel.ZIndex="10" BorderBrush="DodgerBlue" BorderThickness="5">
|
||||
<Border.Effect>
|
||||
<BlurEffect Radius="10" />
|
||||
</Border.Effect>
|
||||
|
@ -18,7 +18,7 @@
|
|||
<EventTrigger RoutedEvent="FrameworkElement.Loaded">
|
||||
<BeginStoryboard>
|
||||
<Storyboard>
|
||||
<DoubleAnimation AutoReverse="True" Storyboard.TargetProperty="Opacity" From="1.0" To="0.2" Duration="0:0:2.5" RepeatBehavior="Forever" />
|
||||
<DoubleAnimation AutoReverse="True" Storyboard.TargetProperty="Opacity" From="0.2" To="1.0" Duration="0:0:2.5" RepeatBehavior="Forever" />
|
||||
</Storyboard>
|
||||
</BeginStoryboard>
|
||||
</EventTrigger>
|
||||
|
@ -41,8 +41,8 @@
|
|||
<Image Grid.Column="0" Grid.ColumnSpan="2" Margin="-25,0,0,0" Source="pack://application:,,,/SafeExamBrowser.UserInterface.Classic;component/Images/SplashScreen.png" />
|
||||
<TextBlock x:Name="InfoTextBlock" Grid.Column="1" Foreground="Gray" Margin="10,75,175,10" TextWrapping="Wrap" />
|
||||
</Grid>
|
||||
<!--<ProgressBar x:Name="ProgressBar" Grid.Row="1" IsIndeterminate="True" BorderThickness="0" />-->
|
||||
<TextBlock x:Name="StatusTextBlock" Grid.Row="1" Text="Application is running..." FontSize="12" FontWeight="Bold" Foreground="Black" HorizontalAlignment="Center" VerticalAlignment="Center" />
|
||||
<ProgressBar x:Name="ProgressBar" Grid.Row="1" IsIndeterminate="True" Background="WhiteSmoke" BorderThickness="0" Foreground="DodgerBlue" Visibility="Hidden" />
|
||||
<TextBlock x:Name="StatusTextBlock" Grid.Row="1" FontSize="12" FontWeight="Bold" Foreground="Black" HorizontalAlignment="Center" Text="{Binding Status}" VerticalAlignment="Center" />
|
||||
<Border Grid.Row="2" BorderBrush="DodgerBlue" BorderThickness="0,0.5,0,0">
|
||||
<ScrollViewer x:Name="LogScrollViewer" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" Margin="0,10,0,0">
|
||||
<ScrollViewer.Resources>
|
||||
|
|
|
@ -9,18 +9,22 @@
|
|||
using System;
|
||||
using System.Windows;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using SafeExamBrowser.Contracts.Configuration;
|
||||
using SafeExamBrowser.Contracts.I18n;
|
||||
using SafeExamBrowser.Contracts.Logging;
|
||||
using SafeExamBrowser.Contracts.UserInterface;
|
||||
using SafeExamBrowser.UserInterface.Classic.ViewModels;
|
||||
|
||||
namespace SafeExamBrowser.UserInterface.Classic
|
||||
{
|
||||
public partial class RuntimeWindow : Window, ILogObserver, IRuntimeWindow
|
||||
public partial class RuntimeWindow : Window, IRuntimeWindow
|
||||
{
|
||||
private bool allowClose;
|
||||
private ILogContentFormatter formatter;
|
||||
private IRuntimeInfo runtimeInfo;
|
||||
private IText text;
|
||||
private RuntimeWindowViewModel model;
|
||||
private WindowClosingEventHandler closing;
|
||||
|
||||
event WindowClosingEventHandler IWindow.Closing
|
||||
|
@ -44,6 +48,20 @@ namespace SafeExamBrowser.UserInterface.Classic
|
|||
Dispatcher.Invoke(Activate);
|
||||
}
|
||||
|
||||
public new void Close()
|
||||
{
|
||||
Dispatcher.Invoke(() =>
|
||||
{
|
||||
allowClose = true;
|
||||
base.Close();
|
||||
});
|
||||
}
|
||||
|
||||
public new void Hide()
|
||||
{
|
||||
Dispatcher.Invoke(base.Hide);
|
||||
}
|
||||
|
||||
public void Notify(ILogContent content)
|
||||
{
|
||||
Dispatcher.Invoke(() =>
|
||||
|
@ -53,18 +71,41 @@ namespace SafeExamBrowser.UserInterface.Classic
|
|||
});
|
||||
}
|
||||
|
||||
public void UpdateStatus(TextKey key)
|
||||
public new void Show()
|
||||
{
|
||||
Dispatcher.Invoke(() => StatusTextBlock.Text = text.Get(key));
|
||||
Dispatcher.Invoke(base.Show);
|
||||
}
|
||||
|
||||
public void UpdateStatus(TextKey key, bool showBusyIndication = false)
|
||||
{
|
||||
Dispatcher.Invoke(() =>
|
||||
{
|
||||
AnimatedBorder.Visibility = showBusyIndication ? Visibility.Hidden : Visibility.Visible;
|
||||
ProgressBar.Visibility = showBusyIndication ? Visibility.Visible : Visibility.Hidden;
|
||||
|
||||
model.StopBusyIndication();
|
||||
model.Status = text.Get(key);
|
||||
|
||||
if (showBusyIndication)
|
||||
{
|
||||
model.StartBusyIndication();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void InitializeRuntimeWindow()
|
||||
{
|
||||
Title = $"{runtimeInfo.ProgramTitle} - Version {runtimeInfo.ProgramVersion}";
|
||||
|
||||
InfoTextBlock.Inlines.Add(new Run($"Version {runtimeInfo.ProgramVersion}") { FontStyle = FontStyles.Italic });
|
||||
InfoTextBlock.Inlines.Add(new LineBreak());
|
||||
InfoTextBlock.Inlines.Add(new LineBreak());
|
||||
InfoTextBlock.Inlines.Add(new Run(runtimeInfo.ProgramCopyright) { FontSize = 10 });
|
||||
|
||||
model = new RuntimeWindowViewModel();
|
||||
StatusTextBlock.DataContext = model;
|
||||
|
||||
Closing += (o, args) => args.Cancel = !allowClose;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -113,10 +113,12 @@
|
|||
</Compile>
|
||||
<Compile Include="UserInterfaceFactory.cs" />
|
||||
<Compile Include="Utilities\IconResourceLoader.cs" />
|
||||
<Compile Include="Utilities\RuntimeWindowLogFormatter.cs" />
|
||||
<Compile Include="Utilities\VisualExtensions.cs" />
|
||||
<Compile Include="Utilities\XamlIconResource.cs" />
|
||||
<Compile Include="ViewModels\DateTimeViewModel.cs" />
|
||||
<Compile Include="ViewModels\LogViewModel.cs" />
|
||||
<Compile Include="ViewModels\RuntimeWindowViewModel.cs" />
|
||||
<Compile Include="ViewModels\SplashScreenViewModel.cs" />
|
||||
<Page Include="AboutWindow.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
|
|
|
@ -17,9 +17,17 @@ namespace SafeExamBrowser.UserInterface.Classic
|
|||
{
|
||||
public partial class SplashScreen : Window, ISplashScreen
|
||||
{
|
||||
private bool allowClose;
|
||||
private SplashScreenViewModel model = new SplashScreenViewModel();
|
||||
private IRuntimeInfo runtimeInfo;
|
||||
private IText text;
|
||||
private WindowClosingEventHandler closing;
|
||||
|
||||
event WindowClosingEventHandler IWindow.Closing
|
||||
{
|
||||
add { closing += value; }
|
||||
remove { closing -= value; }
|
||||
}
|
||||
|
||||
public SplashScreen(IRuntimeInfo runtimeInfo, IText text)
|
||||
{
|
||||
|
@ -30,14 +38,28 @@ namespace SafeExamBrowser.UserInterface.Classic
|
|||
InitializeSplashScreen();
|
||||
}
|
||||
|
||||
public void InvokeClose()
|
||||
public void BringToForeground()
|
||||
{
|
||||
Dispatcher.Invoke(Close);
|
||||
Dispatcher.Invoke(Activate);
|
||||
}
|
||||
|
||||
public void InvokeShow()
|
||||
public new void Close()
|
||||
{
|
||||
Dispatcher.Invoke(Show);
|
||||
Dispatcher.Invoke(() =>
|
||||
{
|
||||
allowClose = true;
|
||||
base.Close();
|
||||
});
|
||||
}
|
||||
|
||||
public new void Hide()
|
||||
{
|
||||
Dispatcher.Invoke(base.Hide);
|
||||
}
|
||||
|
||||
public new void Show()
|
||||
{
|
||||
Dispatcher.Invoke(base.Show);
|
||||
}
|
||||
|
||||
public void Progress(int amount = 1)
|
||||
|
@ -83,6 +105,8 @@ namespace SafeExamBrowser.UserInterface.Classic
|
|||
|
||||
// To prevent the progress bar going from max to min value at startup...
|
||||
model.MaxProgress = 1;
|
||||
|
||||
Closing += (o, args) => args.Cancel = !allowClose;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ using SafeExamBrowser.Contracts.Logging;
|
|||
using SafeExamBrowser.Contracts.UserInterface;
|
||||
using SafeExamBrowser.Contracts.UserInterface.Taskbar;
|
||||
using SafeExamBrowser.UserInterface.Classic.Controls;
|
||||
using SafeExamBrowser.UserInterface.Classic.Utilities;
|
||||
using MessageBoxResult = SafeExamBrowser.Contracts.UserInterface.MessageBoxResult;
|
||||
|
||||
namespace SafeExamBrowser.UserInterface.Classic
|
||||
|
@ -76,6 +77,30 @@ namespace SafeExamBrowser.UserInterface.Classic
|
|||
return new PowerSupplyControl();
|
||||
}
|
||||
|
||||
public IRuntimeWindow CreateRuntimeWindow(IRuntimeInfo runtimeInfo, IText text)
|
||||
{
|
||||
RuntimeWindow runtimeWindow = null;
|
||||
var windowReadyEvent = new AutoResetEvent(false);
|
||||
var runtimeWindowThread = new Thread(() =>
|
||||
{
|
||||
runtimeWindow = new RuntimeWindow(new RuntimeWindowLogFormatter(), runtimeInfo, text);
|
||||
runtimeWindow.Closed += (o, args) => runtimeWindow.Dispatcher.InvokeShutdown();
|
||||
|
||||
windowReadyEvent.Set();
|
||||
|
||||
System.Windows.Threading.Dispatcher.Run();
|
||||
});
|
||||
|
||||
runtimeWindowThread.SetApartmentState(ApartmentState.STA);
|
||||
runtimeWindowThread.Name = nameof(RuntimeWindow);
|
||||
runtimeWindowThread.IsBackground = true;
|
||||
runtimeWindowThread.Start();
|
||||
|
||||
windowReadyEvent.WaitOne();
|
||||
|
||||
return runtimeWindow;
|
||||
}
|
||||
|
||||
public ISplashScreen CreateSplashScreen(IRuntimeInfo runtimeInfo, IText text)
|
||||
{
|
||||
SplashScreen splashScreen = null;
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* Copyright (c) 2018 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.Contracts.Logging;
|
||||
|
||||
namespace SafeExamBrowser.UserInterface.Classic.Utilities
|
||||
{
|
||||
internal class RuntimeWindowLogFormatter : ILogContentFormatter
|
||||
{
|
||||
public string Format(ILogContent content)
|
||||
{
|
||||
if (content is ILogText)
|
||||
{
|
||||
return (content as ILogText).Text;
|
||||
}
|
||||
|
||||
if (content is ILogMessage)
|
||||
{
|
||||
return FormatLogMessage(content as ILogMessage);
|
||||
}
|
||||
|
||||
throw new NotImplementedException($"The runtime window formatter is not yet implemented for log content of type {content.GetType()}!");
|
||||
}
|
||||
|
||||
private string FormatLogMessage(ILogMessage message)
|
||||
{
|
||||
var time = message.DateTime.ToString("HH:mm:ss.fff");
|
||||
var severity = message.Severity.ToString().ToUpper();
|
||||
|
||||
return $"{time} - {severity}: {message.Message}";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* Copyright (c) 2018 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.ComponentModel;
|
||||
using System.Timers;
|
||||
|
||||
namespace SafeExamBrowser.UserInterface.Classic.ViewModels
|
||||
{
|
||||
internal class RuntimeWindowViewModel : INotifyPropertyChanged
|
||||
{
|
||||
private string status;
|
||||
private Timer timer;
|
||||
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
public string Status
|
||||
{
|
||||
get
|
||||
{
|
||||
return status;
|
||||
}
|
||||
set
|
||||
{
|
||||
status = value;
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Status)));
|
||||
}
|
||||
}
|
||||
|
||||
public void StartBusyIndication()
|
||||
{
|
||||
StopBusyIndication();
|
||||
|
||||
timer = new Timer
|
||||
{
|
||||
AutoReset = true,
|
||||
Interval = 750
|
||||
};
|
||||
|
||||
timer.Elapsed += Timer_Elapsed;
|
||||
timer.Start();
|
||||
}
|
||||
|
||||
public void StopBusyIndication()
|
||||
{
|
||||
timer?.Stop();
|
||||
timer?.Close();
|
||||
}
|
||||
|
||||
private void Timer_Elapsed(object sender, ElapsedEventArgs e)
|
||||
{
|
||||
var next = Status ?? string.Empty;
|
||||
|
||||
if (next.EndsWith("..."))
|
||||
{
|
||||
next = Status.Substring(0, Status.Length - 3);
|
||||
}
|
||||
else
|
||||
{
|
||||
next += ".";
|
||||
}
|
||||
|
||||
Status = next;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -53,12 +53,30 @@ namespace SafeExamBrowser.UserInterface.Windows10
|
|||
|
||||
public void BringToForeground()
|
||||
{
|
||||
if (WindowState == WindowState.Minimized)
|
||||
Dispatcher.Invoke(() =>
|
||||
{
|
||||
WindowState = WindowState.Normal;
|
||||
}
|
||||
if (WindowState == WindowState.Minimized)
|
||||
{
|
||||
WindowState = WindowState.Normal;
|
||||
}
|
||||
|
||||
Activate();
|
||||
Activate();
|
||||
});
|
||||
}
|
||||
|
||||
public new void Close()
|
||||
{
|
||||
Dispatcher.Invoke(base.Close);
|
||||
}
|
||||
|
||||
public new void Hide()
|
||||
{
|
||||
Dispatcher.Invoke(base.Hide);
|
||||
}
|
||||
|
||||
public new void Show()
|
||||
{
|
||||
Dispatcher.Invoke(base.Show);
|
||||
}
|
||||
|
||||
public void UpdateAddress(string url)
|
||||
|
|
|
@ -47,6 +47,11 @@ namespace SafeExamBrowser.UserInterface.Windows10
|
|||
Dispatcher.Invoke(base.Close);
|
||||
}
|
||||
|
||||
public new void Hide()
|
||||
{
|
||||
Dispatcher.Invoke(base.Hide);
|
||||
}
|
||||
|
||||
public new void Show()
|
||||
{
|
||||
Dispatcher.Invoke(base.Show);
|
||||
|
|
|
@ -17,9 +17,17 @@ namespace SafeExamBrowser.UserInterface.Windows10
|
|||
{
|
||||
public partial class SplashScreen : Window, ISplashScreen
|
||||
{
|
||||
private bool allowClose;
|
||||
private SplashScreenViewModel model = new SplashScreenViewModel();
|
||||
private IRuntimeInfo runtimeInfo;
|
||||
private IText text;
|
||||
private WindowClosingEventHandler closing;
|
||||
|
||||
event WindowClosingEventHandler IWindow.Closing
|
||||
{
|
||||
add { closing += value; }
|
||||
remove { closing -= value; }
|
||||
}
|
||||
|
||||
public SplashScreen(IRuntimeInfo runtimeInfo, IText text)
|
||||
{
|
||||
|
@ -30,14 +38,28 @@ namespace SafeExamBrowser.UserInterface.Windows10
|
|||
InitializeSplashScreen();
|
||||
}
|
||||
|
||||
public void InvokeClose()
|
||||
public void BringToForeground()
|
||||
{
|
||||
Dispatcher.Invoke(Close);
|
||||
Dispatcher.Invoke(Activate);
|
||||
}
|
||||
|
||||
public void InvokeShow()
|
||||
public new void Close()
|
||||
{
|
||||
Dispatcher.Invoke(Show);
|
||||
Dispatcher.Invoke(() =>
|
||||
{
|
||||
allowClose = true;
|
||||
base.Close();
|
||||
});
|
||||
}
|
||||
|
||||
public new void Hide()
|
||||
{
|
||||
Dispatcher.Invoke(base.Hide);
|
||||
}
|
||||
|
||||
public new void Show()
|
||||
{
|
||||
Dispatcher.Invoke(base.Show);
|
||||
}
|
||||
|
||||
public void Progress(int amount = 1)
|
||||
|
@ -83,6 +105,8 @@ namespace SafeExamBrowser.UserInterface.Windows10
|
|||
|
||||
// To prevent the progress bar going from max to min value at startup...
|
||||
model.MaxProgress = 1;
|
||||
|
||||
Closing += (o, args) => args.Cancel = !allowClose;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -77,6 +77,12 @@ namespace SafeExamBrowser.UserInterface.Windows10
|
|||
return new PowerSupplyControl();
|
||||
}
|
||||
|
||||
public IRuntimeWindow CreateRuntimeWindow(IRuntimeInfo runtimeInfo, IText text)
|
||||
{
|
||||
// TODO
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
public ISplashScreen CreateSplashScreen(IRuntimeInfo runtimeInfo, IText text)
|
||||
{
|
||||
SplashScreen splashScreen = null;
|
||||
|
|
Loading…
Reference in a new issue