SEBWIN-219: Finally found appropriate solution for handling bootstrap- and session-operations.

This commit is contained in:
dbuechel 2018-02-02 09:18:35 +01:00
parent 196836b7eb
commit 5b57734406
46 changed files with 559 additions and 393 deletions

View file

@ -23,7 +23,6 @@ namespace SafeExamBrowser.Client.UnitTests.Behaviour.Operations
private Mock<IApplicationController> controllerMock;
private Mock<IApplicationInfo> appInfoMock;
private Mock<ILogger> loggerMock;
private Mock<ISplashScreen> splashScreenMock;
private Mock<ITaskbar> taskbarMock;
private Mock<IUserInterfaceFactory> uiFactoryMock;
@ -35,14 +34,10 @@ namespace SafeExamBrowser.Client.UnitTests.Behaviour.Operations
controllerMock = new Mock<IApplicationController>();
appInfoMock = new Mock<IApplicationInfo>();
loggerMock = new Mock<ILogger>();
splashScreenMock = new Mock<ISplashScreen>();
taskbarMock = new Mock<ITaskbar>();
uiFactoryMock = new Mock<IUserInterfaceFactory>();
sut = new BrowserOperation(controllerMock.Object, appInfoMock.Object, loggerMock.Object, taskbarMock.Object, uiFactoryMock.Object)
{
SplashScreen = splashScreenMock.Object
};
sut = new BrowserOperation(controllerMock.Object, appInfoMock.Object, loggerMock.Object, taskbarMock.Object, uiFactoryMock.Object);
}
[TestMethod]

View file

@ -11,7 +11,6 @@ using Moq;
using SafeExamBrowser.Client.Behaviour.Operations;
using SafeExamBrowser.Contracts.Behaviour;
using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.UserInterface;
namespace SafeExamBrowser.Client.UnitTests.Behaviour.Operations
{
@ -20,7 +19,6 @@ namespace SafeExamBrowser.Client.UnitTests.Behaviour.Operations
{
private Mock<ILogger> loggerMock;
private Mock<IClientController> clientControllerMock;
private Mock<ISplashScreen> splashScreenMock;
private ClientControllerOperation sut;
@ -29,12 +27,8 @@ namespace SafeExamBrowser.Client.UnitTests.Behaviour.Operations
{
loggerMock = new Mock<ILogger>();
clientControllerMock = new Mock<IClientController>();
splashScreenMock = new Mock<ISplashScreen>();
sut = new ClientControllerOperation(clientControllerMock.Object, loggerMock.Object)
{
SplashScreen = splashScreenMock.Object
};
sut = new ClientControllerOperation(clientControllerMock.Object, loggerMock.Object);
}
[TestMethod]

View file

@ -10,7 +10,6 @@ using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using SafeExamBrowser.Client.Behaviour.Operations;
using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.UserInterface;
using SafeExamBrowser.Contracts.WindowsApi;
namespace SafeExamBrowser.Client.UnitTests.Behaviour.Operations
@ -20,7 +19,6 @@ namespace SafeExamBrowser.Client.UnitTests.Behaviour.Operations
{
private Mock<ILogger> loggerMock;
private Mock<INativeMethods> nativeMethodsMock;
private Mock<ISplashScreen> splashScreenMock;
private ClipboardOperation sut;
@ -29,12 +27,8 @@ namespace SafeExamBrowser.Client.UnitTests.Behaviour.Operations
{
loggerMock = new Mock<ILogger>();
nativeMethodsMock = new Mock<INativeMethods>();
splashScreenMock = new Mock<ISplashScreen>();
sut = new ClipboardOperation(loggerMock.Object, nativeMethodsMock.Object)
{
SplashScreen = splashScreenMock.Object
};
sut = new ClipboardOperation(loggerMock.Object, nativeMethodsMock.Object);
}
[TestMethod]

View file

@ -11,7 +11,6 @@ using Moq;
using SafeExamBrowser.Client.Behaviour.Operations;
using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.Monitoring;
using SafeExamBrowser.Contracts.UserInterface;
using SafeExamBrowser.Contracts.UserInterface.Taskbar;
namespace SafeExamBrowser.Client.UnitTests.Behaviour.Operations
@ -21,7 +20,6 @@ namespace SafeExamBrowser.Client.UnitTests.Behaviour.Operations
{
private Mock<IDisplayMonitor> displayMonitorMock;
private Mock<ILogger> loggerMock;
private Mock<ISplashScreen> splashScreenMock;
private Mock<ITaskbar> taskbarMock;
private DisplayMonitorOperation sut;
@ -31,13 +29,9 @@ namespace SafeExamBrowser.Client.UnitTests.Behaviour.Operations
{
loggerMock = new Mock<ILogger>();
displayMonitorMock = new Mock<IDisplayMonitor>();
splashScreenMock = new Mock<ISplashScreen>();
taskbarMock = new Mock<ITaskbar>();
sut = new DisplayMonitorOperation(displayMonitorMock.Object, loggerMock.Object, taskbarMock.Object)
{
SplashScreen = splashScreenMock.Object
};
sut = new DisplayMonitorOperation(displayMonitorMock.Object, loggerMock.Object, taskbarMock.Object);
}
[TestMethod]

View file

@ -11,7 +11,6 @@ using Moq;
using SafeExamBrowser.Client.Behaviour.Operations;
using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.Monitoring;
using SafeExamBrowser.Contracts.UserInterface;
using SafeExamBrowser.Contracts.WindowsApi;
namespace SafeExamBrowser.Client.UnitTests.Behaviour.Operations
@ -22,7 +21,6 @@ namespace SafeExamBrowser.Client.UnitTests.Behaviour.Operations
private Mock<IKeyboardInterceptor> keyboardInterceptorMock;
private Mock<ILogger> loggerMock;
private Mock<INativeMethods> nativeMethodsMock;
private Mock<ISplashScreen> splashScreenMock;
private KeyboardInterceptorOperation sut;
@ -32,12 +30,8 @@ namespace SafeExamBrowser.Client.UnitTests.Behaviour.Operations
keyboardInterceptorMock = new Mock<IKeyboardInterceptor>();
loggerMock = new Mock<ILogger>();
nativeMethodsMock = new Mock<INativeMethods>();
splashScreenMock = new Mock<ISplashScreen>();
sut = new KeyboardInterceptorOperation(keyboardInterceptorMock.Object, loggerMock.Object, nativeMethodsMock.Object)
{
SplashScreen = splashScreenMock.Object
};
sut = new KeyboardInterceptorOperation(keyboardInterceptorMock.Object, loggerMock.Object, nativeMethodsMock.Object);
}
[TestMethod]

View file

@ -11,7 +11,6 @@ using Moq;
using SafeExamBrowser.Client.Behaviour.Operations;
using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.Monitoring;
using SafeExamBrowser.Contracts.UserInterface;
using SafeExamBrowser.Contracts.WindowsApi;
namespace SafeExamBrowser.Client.UnitTests.Behaviour.Operations
@ -22,7 +21,6 @@ namespace SafeExamBrowser.Client.UnitTests.Behaviour.Operations
private Mock<IMouseInterceptor> mouseInterceptorMock;
private Mock<ILogger> loggerMock;
private Mock<INativeMethods> nativeMethodsMock;
private Mock<ISplashScreen> splashScreenMock;
private MouseInterceptorOperation sut;
@ -32,12 +30,8 @@ namespace SafeExamBrowser.Client.UnitTests.Behaviour.Operations
mouseInterceptorMock = new Mock<IMouseInterceptor>();
loggerMock = new Mock<ILogger>();
nativeMethodsMock = new Mock<INativeMethods>();
splashScreenMock = new Mock<ISplashScreen>();
sut = new MouseInterceptorOperation(loggerMock.Object, mouseInterceptorMock.Object, nativeMethodsMock.Object)
{
SplashScreen = splashScreenMock.Object
};
sut = new MouseInterceptorOperation(loggerMock.Object, mouseInterceptorMock.Object, nativeMethodsMock.Object);
}
[TestMethod]

View file

@ -11,7 +11,6 @@ using Moq;
using SafeExamBrowser.Client.Behaviour.Operations;
using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.Monitoring;
using SafeExamBrowser.Contracts.UserInterface;
namespace SafeExamBrowser.Client.UnitTests.Behaviour.Operations
{
@ -20,7 +19,6 @@ namespace SafeExamBrowser.Client.UnitTests.Behaviour.Operations
{
private Mock<ILogger> loggerMock;
private Mock<IProcessMonitor> processMonitorMock;
private Mock<ISplashScreen> splashScreenMock;
private ProcessMonitorOperation sut;
@ -29,12 +27,8 @@ namespace SafeExamBrowser.Client.UnitTests.Behaviour.Operations
{
loggerMock = new Mock<ILogger>();
processMonitorMock = new Mock<IProcessMonitor>();
splashScreenMock = new Mock<ISplashScreen>();
sut = new ProcessMonitorOperation(loggerMock.Object, processMonitorMock.Object)
{
SplashScreen = splashScreenMock.Object
};
sut = new ProcessMonitorOperation(loggerMock.Object, processMonitorMock.Object);
}
[TestMethod]

View file

@ -24,7 +24,6 @@ namespace SafeExamBrowser.Client.UnitTests.Behaviour.Operations
{
private Mock<ILogger> loggerMock;
private Mock<ITaskbarSettings> settingsMock;
private Mock<ISplashScreen> splashScreenMock;
private Mock<ISystemComponent<ISystemKeyboardLayoutControl>> keyboardLayoutMock;
private Mock<ISystemComponent<ISystemPowerSupplyControl>> powerSupplyMock;
private Mock<ISystemComponent<ISystemWirelessNetworkControl>> wirelessNetworkMock;
@ -40,7 +39,6 @@ namespace SafeExamBrowser.Client.UnitTests.Behaviour.Operations
{
loggerMock = new Mock<ILogger>();
settingsMock = new Mock<ITaskbarSettings>();
splashScreenMock = new Mock<ISplashScreen>();
keyboardLayoutMock = new Mock<ISystemComponent<ISystemKeyboardLayoutControl>>();
powerSupplyMock = new Mock<ISystemComponent<ISystemPowerSupplyControl>>();
wirelessNetworkMock = new Mock<ISystemComponent<ISystemWirelessNetworkControl>>();
@ -64,10 +62,7 @@ namespace SafeExamBrowser.Client.UnitTests.Behaviour.Operations
systemInfoMock.Object,
taskbarMock.Object,
textMock.Object,
uiFactoryMock.Object)
{
SplashScreen = splashScreenMock.Object
};
uiFactoryMock.Object);
}
[TestMethod]

View file

@ -11,7 +11,6 @@ using Moq;
using SafeExamBrowser.Client.Behaviour.Operations;
using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.Monitoring;
using SafeExamBrowser.Contracts.UserInterface;
namespace SafeExamBrowser.Client.UnitTests.Behaviour.Operations
{
@ -19,7 +18,6 @@ namespace SafeExamBrowser.Client.UnitTests.Behaviour.Operations
public class WindowMonitorOperationTests
{
private Mock<ILogger> loggerMock;
private Mock<ISplashScreen> splashScreenMock;
private Mock<IWindowMonitor> windowMonitorMock;
private WindowMonitorOperation sut;
@ -28,13 +26,9 @@ namespace SafeExamBrowser.Client.UnitTests.Behaviour.Operations
public void Initialize()
{
loggerMock = new Mock<ILogger>();
splashScreenMock = new Mock<ISplashScreen>();
windowMonitorMock = new Mock<IWindowMonitor>();
sut = new WindowMonitorOperation(loggerMock.Object, windowMonitorMock.Object)
{
SplashScreen = splashScreenMock.Object
};
sut = new WindowMonitorOperation(loggerMock.Object, windowMonitorMock.Object);
}
[TestMethod]

View file

@ -25,7 +25,7 @@ namespace SafeExamBrowser.Client.Behaviour.Operations
private IUserInterfaceFactory uiFactory;
public bool Abort { get; private set; }
public ISplashScreen SplashScreen { private get; set; }
public IProgressIndicator ProgressIndicator { private get; set; }
public BrowserOperation(
IApplicationController browserController,
@ -44,7 +44,7 @@ namespace SafeExamBrowser.Client.Behaviour.Operations
public void Perform()
{
logger.Info("Initializing browser...");
SplashScreen.UpdateText(TextKey.SplashScreen_InitializeBrowser, true);
ProgressIndicator?.UpdateText(TextKey.SplashScreen_InitializeBrowser, true);
var browserButton = uiFactory.CreateApplicationButton(browserInfo);
@ -62,7 +62,7 @@ namespace SafeExamBrowser.Client.Behaviour.Operations
public void Revert()
{
logger.Info("Terminating browser...");
SplashScreen.UpdateText(TextKey.SplashScreen_TerminateBrowser, true);
ProgressIndicator?.UpdateText(TextKey.SplashScreen_TerminateBrowser, true);
browserController.Terminate();
}

View file

@ -20,7 +20,7 @@ namespace SafeExamBrowser.Client.Behaviour.Operations
private IClientController controller;
public bool Abort { get; private set; }
public ISplashScreen SplashScreen { private get; set; }
public IProgressIndicator ProgressIndicator { private get; set; }
public ClientControllerOperation(IClientController controller, ILogger logger)
{
@ -31,7 +31,7 @@ namespace SafeExamBrowser.Client.Behaviour.Operations
public void Perform()
{
logger.Info("Starting event handling...");
SplashScreen.UpdateText(TextKey.SplashScreen_StartEventHandling);
ProgressIndicator?.UpdateText(TextKey.SplashScreen_StartEventHandling);
controller.Start();
}
@ -44,7 +44,7 @@ namespace SafeExamBrowser.Client.Behaviour.Operations
public void Revert()
{
logger.Info("Stopping event handling...");
SplashScreen.UpdateText(TextKey.SplashScreen_StopEventHandling);
ProgressIndicator?.UpdateText(TextKey.SplashScreen_StopEventHandling);
controller.Stop();
}

View file

@ -20,7 +20,7 @@ namespace SafeExamBrowser.Client.Behaviour.Operations
private INativeMethods nativeMethods;
public bool Abort { get; private set; }
public ISplashScreen SplashScreen { private get; set; }
public IProgressIndicator ProgressIndicator { private get; set; }
public ClipboardOperation(ILogger logger, INativeMethods nativeMethods)
{
@ -46,7 +46,7 @@ namespace SafeExamBrowser.Client.Behaviour.Operations
private void EmptyClipboard()
{
logger.Info("Emptying clipboard...");
SplashScreen.UpdateText(TextKey.SplashScreen_EmptyClipboard);
ProgressIndicator?.UpdateText(TextKey.SplashScreen_EmptyClipboard);
nativeMethods.EmptyClipboard();
}

View file

@ -22,7 +22,7 @@ namespace SafeExamBrowser.Client.Behaviour.Operations
private ITaskbar taskbar;
public bool Abort { get; private set; }
public ISplashScreen SplashScreen { private get; set; }
public IProgressIndicator ProgressIndicator { private get; set; }
public DisplayMonitorOperation(IDisplayMonitor displayMonitor, ILogger logger, ITaskbar taskbar)
{
@ -34,7 +34,7 @@ namespace SafeExamBrowser.Client.Behaviour.Operations
public void Perform()
{
logger.Info("Initializing working area...");
SplashScreen.UpdateText(TextKey.SplashScreen_InitializeWorkingArea);
ProgressIndicator?.UpdateText(TextKey.SplashScreen_InitializeWorkingArea);
displayMonitor.PreventSleepMode();
displayMonitor.InitializePrimaryDisplay(taskbar.GetAbsoluteHeight());
@ -49,7 +49,7 @@ namespace SafeExamBrowser.Client.Behaviour.Operations
public void Revert()
{
logger.Info("Restoring working area...");
SplashScreen.UpdateText(TextKey.SplashScreen_RestoreWorkingArea);
ProgressIndicator?.UpdateText(TextKey.SplashScreen_RestoreWorkingArea);
displayMonitor.StopMonitoringDisplayChanges();
displayMonitor.ResetPrimaryDisplay();

View file

@ -22,7 +22,7 @@ namespace SafeExamBrowser.Client.Behaviour.Operations
private INativeMethods nativeMethods;
public bool Abort { get; private set; }
public ISplashScreen SplashScreen { private get; set; }
public IProgressIndicator ProgressIndicator { private get; set; }
public KeyboardInterceptorOperation(
IKeyboardInterceptor keyboardInterceptor,
@ -37,7 +37,7 @@ namespace SafeExamBrowser.Client.Behaviour.Operations
public void Perform()
{
logger.Info("Starting keyboard interception...");
SplashScreen.UpdateText(TextKey.SplashScreen_StartKeyboardInterception);
ProgressIndicator?.UpdateText(TextKey.SplashScreen_StartKeyboardInterception);
nativeMethods.RegisterKeyboardHook(keyboardInterceptor);
}
@ -50,7 +50,7 @@ namespace SafeExamBrowser.Client.Behaviour.Operations
public void Revert()
{
logger.Info("Stopping keyboard interception...");
SplashScreen.UpdateText(TextKey.SplashScreen_StopKeyboardInterception);
ProgressIndicator?.UpdateText(TextKey.SplashScreen_StopKeyboardInterception);
nativeMethods.DeregisterKeyboardHook(keyboardInterceptor);
}

View file

@ -22,7 +22,7 @@ namespace SafeExamBrowser.Client.Behaviour.Operations
private INativeMethods nativeMethods;
public bool Abort { get; private set; }
public ISplashScreen SplashScreen { private get; set; }
public IProgressIndicator ProgressIndicator { private get; set; }
public MouseInterceptorOperation(
ILogger logger,
@ -37,7 +37,7 @@ namespace SafeExamBrowser.Client.Behaviour.Operations
public void Perform()
{
logger.Info("Starting mouse interception...");
SplashScreen.UpdateText(TextKey.SplashScreen_StartMouseInterception);
ProgressIndicator?.UpdateText(TextKey.SplashScreen_StartMouseInterception);
nativeMethods.RegisterMouseHook(mouseInterceptor);
}
@ -50,7 +50,7 @@ namespace SafeExamBrowser.Client.Behaviour.Operations
public void Revert()
{
logger.Info("Stopping mouse interception...");
SplashScreen.UpdateText(TextKey.SplashScreen_StopMouseInterception);
ProgressIndicator?.UpdateText(TextKey.SplashScreen_StopMouseInterception);
nativeMethods.DeregisterMouseHook(mouseInterceptor);
}

View file

@ -20,7 +20,7 @@ namespace SafeExamBrowser.Client.Behaviour.Operations
private IProcessMonitor processMonitor;
public bool Abort { get; private set; }
public ISplashScreen SplashScreen { private get; set; }
public IProgressIndicator ProgressIndicator { private get; set; }
public ProcessMonitorOperation(ILogger logger, IProcessMonitor processMonitor)
{
@ -31,12 +31,12 @@ namespace SafeExamBrowser.Client.Behaviour.Operations
public void Perform()
{
logger.Info("Initializing process monitoring...");
SplashScreen.UpdateText(TextKey.SplashScreen_WaitExplorerTermination, true);
ProgressIndicator?.UpdateText(TextKey.SplashScreen_WaitExplorerTermination, true);
processMonitor.CloseExplorerShell();
processMonitor.StartMonitoringExplorer();
SplashScreen.UpdateText(TextKey.SplashScreen_InitializeProcessMonitoring);
ProgressIndicator?.UpdateText(TextKey.SplashScreen_InitializeProcessMonitoring);
// TODO
}
@ -49,11 +49,11 @@ namespace SafeExamBrowser.Client.Behaviour.Operations
public void Revert()
{
logger.Info("Stopping process monitoring...");
SplashScreen.UpdateText(TextKey.SplashScreen_StopProcessMonitoring);
ProgressIndicator?.UpdateText(TextKey.SplashScreen_StopProcessMonitoring);
// TODO
SplashScreen.UpdateText(TextKey.SplashScreen_WaitExplorerStartup, true);
ProgressIndicator?.UpdateText(TextKey.SplashScreen_WaitExplorerStartup, true);
processMonitor.StopMonitoringExplorer();
processMonitor.StartExplorerShell();

View file

@ -33,7 +33,7 @@ namespace SafeExamBrowser.Client.Behaviour.Operations
private IText text;
public bool Abort { get; private set; }
public ISplashScreen SplashScreen { private get; set; }
public IProgressIndicator ProgressIndicator { private get; set; }
public TaskbarOperation(
ILogger logger,
@ -60,7 +60,7 @@ namespace SafeExamBrowser.Client.Behaviour.Operations
public void Perform()
{
logger.Info("Initializing taskbar...");
SplashScreen.UpdateText(TextKey.SplashScreen_InitializeTaskbar);
ProgressIndicator?.UpdateText(TextKey.SplashScreen_InitializeTaskbar);
if (settings.AllowApplicationLog)
{
@ -91,7 +91,7 @@ namespace SafeExamBrowser.Client.Behaviour.Operations
public void Revert()
{
logger.Info("Terminating taskbar...");
SplashScreen.UpdateText(TextKey.SplashScreen_TerminateTaskbar);
ProgressIndicator?.UpdateText(TextKey.SplashScreen_TerminateTaskbar);
if (settings.AllowApplicationLog)
{

View file

@ -20,7 +20,7 @@ namespace SafeExamBrowser.Client.Behaviour.Operations
private IWindowMonitor windowMonitor;
public bool Abort { get; private set; }
public ISplashScreen SplashScreen { private get; set; }
public IProgressIndicator ProgressIndicator { private get; set; }
public WindowMonitorOperation(ILogger logger, IWindowMonitor windowMonitor)
{
@ -31,7 +31,7 @@ namespace SafeExamBrowser.Client.Behaviour.Operations
public void Perform()
{
logger.Info("Initializing window monitoring...");
SplashScreen.UpdateText(TextKey.SplashScreen_InitializeWindowMonitoring);
ProgressIndicator?.UpdateText(TextKey.SplashScreen_InitializeWindowMonitoring);
windowMonitor.HideAllWindows();
windowMonitor.StartMonitoringWindows();
@ -45,7 +45,7 @@ namespace SafeExamBrowser.Client.Behaviour.Operations
public void Revert()
{
logger.Info("Stopping window monitoring...");
SplashScreen.UpdateText(TextKey.SplashScreen_StopWindowMonitoring);
ProgressIndicator?.UpdateText(TextKey.SplashScreen_StopWindowMonitoring);
windowMonitor.StopMonitoringWindows();
windowMonitor.RestoreHiddenWindows();

View file

@ -6,9 +6,6 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
using System.Collections.Generic;
using SafeExamBrowser.Contracts.Behaviour.Operations;
namespace SafeExamBrowser.Contracts.Behaviour
{
public interface IRuntimeController
@ -21,6 +18,6 @@ namespace SafeExamBrowser.Contracts.Behaviour
/// <summary>
/// Tries to start the runtime. Returns <c>true</c> if successful, otherwise <c>false</c>.
/// </summary>
bool TryStart(Queue<IOperation> operations);
bool TryStart();
}
}

View file

@ -13,14 +13,14 @@ namespace SafeExamBrowser.Contracts.Behaviour.Operations
public interface IOperation
{
/// <summary>
/// Determines whether the procedure to which this operation belongs should be aborted.
/// Determines whether the procedure to which this operation belongs to should be aborted.
/// </summary>
bool Abort { get; }
/// <summary>
/// The splash screen to be used to show status information to the user.
/// The progress indicator to be used to show status information to the user. Will be ignored if <c>null</c>.
/// </summary>
ISplashScreen SplashScreen { set; }
IProgressIndicator ProgressIndicator { set; }
/// <summary>
/// Performs the operation.

View file

@ -6,24 +6,29 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
using System.Collections.Generic;
using SafeExamBrowser.Contracts.UserInterface;
namespace SafeExamBrowser.Contracts.Behaviour.Operations
{
public interface IOperationSequence
{
/// <summary>
/// Tries to perform the given sequence of operations. Returns <c>true</c> if the procedure was successful, <c>false</c> otherwise.
/// The progress indicator to be used when performing any action. Will be ignored if <c>null</c>.
/// </summary>
bool TryPerform(Queue<IOperation> operations);
IProgressIndicator ProgressIndicator { set; }
/// <summary>
/// Tries to repeat all operations of this sequence. Returns <c>true</c> if the procedure was successful, <c>false</c> otherwise.
/// Tries to perform the operations of this sequence. Returns <c>true</c> if the procedure was successful, <c>false</c> otherwise.
/// </summary>
bool TryPerform();
/// <summary>
/// Tries to repeat the operations of this sequence. Returns <c>true</c> if the procedure was successful, <c>false</c> otherwise.
/// </summary>
bool TryRepeat();
/// <summary>
/// Tries to revert all operations of this sequence. Returns <c>true</c> if the procedure was successful, <c>false</c> otherwise.
/// Tries to revert the operations of this sequence. Returns <c>true</c> if the procedure was successful, <c>false</c> otherwise.
/// </summary>
bool TryRevert();
}

View file

@ -44,7 +44,6 @@ namespace SafeExamBrowser.Contracts.I18n
SplashScreen_StartEventHandling,
SplashScreen_StartKeyboardInterception,
SplashScreen_StartMouseInterception,
SplashScreen_StartupProcedure,
SplashScreen_StopEventHandling,
SplashScreen_StopKeyboardInterception,
SplashScreen_StopMouseInterception,

View file

@ -108,6 +108,7 @@
<Compile Include="UserInterface\IBrowserControl.cs" />
<Compile Include="UserInterface\IBrowserWindow.cs" />
<Compile Include="UserInterface\IMessageBox.cs" />
<Compile Include="UserInterface\IProgressIndicator.cs" />
<Compile Include="UserInterface\IRuntimeWindow.cs" />
<Compile Include="UserInterface\MessageBoxResult.cs" />
<Compile Include="UserInterface\Taskbar\INotificationButton.cs" />

View file

@ -0,0 +1,45 @@
/*
* 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 SafeExamBrowser.Contracts.I18n;
namespace SafeExamBrowser.Contracts.UserInterface
{
public interface IProgressIndicator
{
/// <summary>
/// Updates the progress value according to the specified amount.
/// </summary>
void Progress(int amount = 1);
/// <summary>
/// Regresses the progress value according to the specified amount.
/// </summary>
void Regress(int amount = 1);
/// <summary>
/// Sets the style of the progress indicator to indeterminate (<c>Progress</c> and <c>Regress</c> won't have any effect when called).
/// </summary>
void SetIndeterminate();
/// <summary>
/// Sets the maximum progress value.
/// </summary>
void SetMaxValue(int max);
/// <summary>
/// Sets the current progress value.
/// </summary>
void SetValue(int value);
/// <summary>
/// Updates the status text. If the busy flag is set, an animation will be shown to indicate a long-running operation.
/// </summary>
void UpdateText(TextKey key, bool showBusyIndication = false);
}
}

View file

@ -6,17 +6,20 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
using SafeExamBrowser.Contracts.I18n;
using SafeExamBrowser.Contracts.Logging;
namespace SafeExamBrowser.Contracts.UserInterface
{
public interface IRuntimeWindow : ILogObserver, IWindow
public interface IRuntimeWindow : ILogObserver, IProgressIndicator, 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.
/// Hides the progress bar.
/// </summary>
void UpdateStatus(TextKey key, bool showBusyIndication = false);
void HideProgressBar();
/// <summary>
/// Shows the progress bar.
/// </summary>
void ShowProgressBar();
}
}

View file

@ -6,37 +6,9 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
using SafeExamBrowser.Contracts.I18n;
namespace SafeExamBrowser.Contracts.UserInterface
{
public interface ISplashScreen : IWindow
public interface ISplashScreen : IProgressIndicator, IWindow
{
/// <summary>
/// Updates the progress bar of the splash screen according to the specified amount.
/// </summary>
void Progress(int amount = 1);
/// <summary>
/// Regresses the progress bar of the splash screen according to the specified amount.
/// </summary>
void Regress(int amount = 1);
/// <summary>
/// Sets the style of the progress bar to indeterminate, i.e. <c>Progress</c> and
/// <c>Regress</c> won't have any effect when called.
/// </summary>
void SetIndeterminate();
/// <summary>
/// Set the maximum of the splash screen's progress bar.
/// </summary>
void SetMaxProgress(int max);
/// <summary>
/// Updates the status text of the splash screen. If the busy flag is set,
/// the splash screen will show an animation to indicate a long-running operation.
/// </summary>
void UpdateText(TextKey key, bool showBusyIndication = false);
}
}

View file

@ -10,7 +10,6 @@ using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using SafeExamBrowser.Contracts.I18n;
using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.UserInterface;
using SafeExamBrowser.Core.Behaviour.Operations;
namespace SafeExamBrowser.Core.UnitTests.Behaviour.Operations
@ -19,7 +18,6 @@ namespace SafeExamBrowser.Core.UnitTests.Behaviour.Operations
public class I18nOperationTests
{
private Mock<ILogger> loggerMock;
private Mock<ISplashScreen> splashScreenMock;
private Mock<IText> textMock;
private I18nOperation sut;
@ -28,13 +26,9 @@ namespace SafeExamBrowser.Core.UnitTests.Behaviour.Operations
public void Initialize()
{
loggerMock = new Mock<ILogger>();
splashScreenMock = new Mock<ISplashScreen>();
textMock = new Mock<IText>();
sut = new I18nOperation(loggerMock.Object, textMock.Object)
{
SplashScreen = splashScreenMock.Object
};
sut = new I18nOperation(loggerMock.Object, textMock.Object);
}
[TestMethod]

View file

@ -11,8 +11,6 @@ using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using SafeExamBrowser.Contracts.Behaviour.Operations;
using SafeExamBrowser.Contracts.Configuration;
using SafeExamBrowser.Contracts.I18n;
using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.UserInterface;
using SafeExamBrowser.Core.Behaviour.Operations;
@ -23,28 +21,17 @@ namespace SafeExamBrowser.Core.UnitTests.Behaviour.Operations
public class OperationSequenceTests
{
private Mock<ILogger> loggerMock;
private Mock<IRuntimeInfo> runtimeInfoMock;
private Mock<IText> textMock;
private Mock<IUserInterfaceFactory> uiFactoryMock;
private IOperationSequence sut;
[TestInitialize]
public void Initialize()
{
loggerMock = new Mock<ILogger>();
runtimeInfoMock = new Mock<IRuntimeInfo>();
textMock = new Mock<IText>();
uiFactoryMock = new Mock<IUserInterfaceFactory>();
uiFactoryMock.Setup(f => f.CreateSplashScreen(runtimeInfoMock.Object, textMock.Object)).Returns(new Mock<ISplashScreen>().Object);
sut = new OperationSequence(loggerMock.Object, runtimeInfoMock.Object, textMock.Object, uiFactoryMock.Object);
}
#region Perform Tests
[TestMethod]
public void MustCorrectlyAbortProcess()
public void MustCorrectlyAbortPerform()
{
var operationA = new Mock<IOperation>();
var operationB = new Mock<IOperation>();
@ -57,7 +44,8 @@ namespace SafeExamBrowser.Core.UnitTests.Behaviour.Operations
operations.Enqueue(operationB.Object);
operations.Enqueue(operationC.Object);
var success = sut.TryPerform(operations);
var sut = new OperationSequence(loggerMock.Object, operations);
var success = sut.TryPerform();
operationA.Verify(o => o.Perform(), Times.Once);
operationA.Verify(o => o.Revert(), Times.Once);
@ -81,7 +69,8 @@ namespace SafeExamBrowser.Core.UnitTests.Behaviour.Operations
operations.Enqueue(operationB.Object);
operations.Enqueue(operationC.Object);
var success = sut.TryPerform(operations);
var sut = new OperationSequence(loggerMock.Object, operations);
var success = sut.TryPerform();
operationA.Verify(o => o.Perform(), Times.Once);
operationA.Verify(o => o.Revert(), Times.Never);
@ -110,7 +99,8 @@ namespace SafeExamBrowser.Core.UnitTests.Behaviour.Operations
operations.Enqueue(operationB.Object);
operations.Enqueue(operationC.Object);
var success = sut.TryPerform(operations);
var sut = new OperationSequence(loggerMock.Object, operations);
var success = sut.TryPerform();
Assert.IsTrue(success);
Assert.IsTrue(a == 1);
@ -134,7 +124,8 @@ namespace SafeExamBrowser.Core.UnitTests.Behaviour.Operations
operations.Enqueue(operationC.Object);
operations.Enqueue(operationD.Object);
var success = sut.TryPerform(operations);
var sut = new OperationSequence(loggerMock.Object, operations);
var success = sut.TryPerform();
operationA.Verify(o => o.Perform(), Times.Once);
operationA.Verify(o => o.Revert(), Times.Once);
@ -169,7 +160,8 @@ namespace SafeExamBrowser.Core.UnitTests.Behaviour.Operations
operations.Enqueue(operationC.Object);
operations.Enqueue(operationD.Object);
var success = sut.TryPerform(operations);
var sut = new OperationSequence(loggerMock.Object, operations);
var success = sut.TryPerform();
Assert.IsFalse(success);
Assert.IsTrue(d == 0);
@ -195,7 +187,8 @@ namespace SafeExamBrowser.Core.UnitTests.Behaviour.Operations
operations.Enqueue(operationB.Object);
operations.Enqueue(operationC.Object);
var result = sut.TryPerform(operations);
var sut = new OperationSequence(loggerMock.Object, operations);
var success = sut.TryPerform();
operationA.Verify(o => o.Perform(), Times.Once);
operationA.Verify(o => o.Revert(), Times.Once);
@ -208,18 +201,23 @@ namespace SafeExamBrowser.Core.UnitTests.Behaviour.Operations
[TestMethod]
public void MustSucceedWithEmptyQueue()
{
var result = sut.TryPerform(new Queue<IOperation>());
var sut = new OperationSequence(loggerMock.Object, new Queue<IOperation>());
var success = sut.TryPerform();
Assert.IsTrue(result);
Assert.IsTrue(success);
}
[TestMethod]
public void MustNotFailInCaseOfUnexpectedError()
{
uiFactoryMock.Setup(l => l.CreateSplashScreen(It.IsAny<IRuntimeInfo>(), It.IsAny<IText>())).Throws(new Exception());
var sut = new OperationSequence(loggerMock.Object, new Queue<IOperation>());
var indicatorMock = new Mock<IProgressIndicator>();
var success = sut.TryPerform(new Queue<IOperation>());
indicatorMock.Setup(i => i.SetMaxValue(It.IsAny<int>())).Throws<Exception>();
sut.ProgressIndicator = indicatorMock.Object;
var success = sut.TryPerform();
Assert.IsFalse(success);
}
@ -229,10 +227,147 @@ namespace SafeExamBrowser.Core.UnitTests.Behaviour.Operations
#region Repeat Tests
[TestMethod]
public void Fail()
public void MustCorrectlyAbortRepeat()
{
// TODO
Assert.Fail();
var operationA = new Mock<IOperation>();
var operationB = new Mock<IOperation>();
var operationC = new Mock<IOperation>();
var operations = new Queue<IOperation>();
operationB.SetupGet(o => o.Abort).Returns(true);
operations.Enqueue(operationA.Object);
operations.Enqueue(operationB.Object);
operations.Enqueue(operationC.Object);
var sut = new OperationSequence(loggerMock.Object, operations);
var success = sut.TryRepeat();
operationA.Verify(o => o.Repeat(), Times.Once);
operationA.Verify(o => o.Revert(), Times.Never);
operationB.Verify(o => o.Repeat(), Times.Once);
operationB.Verify(o => o.Revert(), Times.Never);
operationC.Verify(o => o.Repeat(), Times.Never);
operationC.Verify(o => o.Revert(), Times.Never);
Assert.IsFalse(success);
}
[TestMethod]
public void MustRepeatOperations()
{
var operationA = new Mock<IOperation>();
var operationB = new Mock<IOperation>();
var operationC = new Mock<IOperation>();
var operations = new Queue<IOperation>();
operations.Enqueue(operationA.Object);
operations.Enqueue(operationB.Object);
operations.Enqueue(operationC.Object);
var sut = new OperationSequence(loggerMock.Object, operations);
var success = sut.TryRepeat();
operationA.Verify(o => o.Perform(), Times.Never);
operationA.Verify(o => o.Repeat(), Times.Once);
operationA.Verify(o => o.Revert(), Times.Never);
operationB.Verify(o => o.Perform(), Times.Never);
operationB.Verify(o => o.Repeat(), Times.Once);
operationB.Verify(o => o.Revert(), Times.Never);
operationC.Verify(o => o.Perform(), Times.Never);
operationC.Verify(o => o.Repeat(), Times.Once);
operationC.Verify(o => o.Revert(), Times.Never);
Assert.IsTrue(success);
}
[TestMethod]
public void MustRepeatOperationsInSequence()
{
int current = 0, a = 0, b = 0, c = 0;
var operationA = new Mock<IOperation>();
var operationB = new Mock<IOperation>();
var operationC = new Mock<IOperation>();
var operations = new Queue<IOperation>();
operationA.Setup(o => o.Repeat()).Callback(() => a = ++current);
operationB.Setup(o => o.Repeat()).Callback(() => b = ++current);
operationC.Setup(o => o.Repeat()).Callback(() => c = ++current);
operations.Enqueue(operationA.Object);
operations.Enqueue(operationB.Object);
operations.Enqueue(operationC.Object);
var sut = new OperationSequence(loggerMock.Object, operations);
var success = sut.TryRepeat();
Assert.IsTrue(success);
Assert.IsTrue(a == 1);
Assert.IsTrue(b == 2);
Assert.IsTrue(c == 3);
}
[TestMethod]
public void MustNotRevertOperationsInCaseOfError()
{
var operationA = new Mock<IOperation>();
var operationB = new Mock<IOperation>();
var operationC = new Mock<IOperation>();
var operationD = new Mock<IOperation>();
var operations = new Queue<IOperation>();
operationC.Setup(o => o.Repeat()).Throws<Exception>();
operations.Enqueue(operationA.Object);
operations.Enqueue(operationB.Object);
operations.Enqueue(operationC.Object);
operations.Enqueue(operationD.Object);
var sut = new OperationSequence(loggerMock.Object, operations);
var success = sut.TryRepeat();
operationA.Verify(o => o.Repeat(), Times.Once);
operationA.Verify(o => o.Revert(), Times.Never);
operationB.Verify(o => o.Repeat(), Times.Once);
operationB.Verify(o => o.Revert(), Times.Never);
operationC.Verify(o => o.Repeat(), Times.Once);
operationC.Verify(o => o.Revert(), Times.Never);
operationD.Verify(o => o.Repeat(), Times.Never);
operationD.Verify(o => o.Revert(), Times.Never);
Assert.IsFalse(success);
}
[TestMethod]
public void MustSucceedRepeatingWithEmptyQueue()
{
var sut = new OperationSequence(loggerMock.Object, new Queue<IOperation>());
var success = sut.TryRepeat();
Assert.IsTrue(success);
}
[TestMethod]
public void MustSucceedRepeatingWithoutCallingPerform()
{
var sut = new OperationSequence(loggerMock.Object, new Queue<IOperation>());
var success = sut.TryRepeat();
Assert.IsTrue(success);
}
[TestMethod]
public void MustNotFailInCaseOfUnexpectedErrorWhenRepeating()
{
var sut = new OperationSequence(loggerMock.Object, new Queue<IOperation>());
var indicatorMock = new Mock<IProgressIndicator>();
indicatorMock.Setup(i => i.SetMaxValue(It.IsAny<int>())).Throws<Exception>();
sut.ProgressIndicator = indicatorMock.Object;
var success = sut.TryRepeat();
Assert.IsFalse(success);
}
#endregion
@ -251,7 +386,9 @@ namespace SafeExamBrowser.Core.UnitTests.Behaviour.Operations
operations.Enqueue(operationB.Object);
operations.Enqueue(operationC.Object);
sut.TryPerform(operations);
var sut = new OperationSequence(loggerMock.Object, operations);
sut.TryPerform();
var success = sut.TryRevert();
@ -279,7 +416,9 @@ namespace SafeExamBrowser.Core.UnitTests.Behaviour.Operations
operations.Enqueue(operationB.Object);
operations.Enqueue(operationC.Object);
sut.TryPerform(operations);
var sut = new OperationSequence(loggerMock.Object, operations);
sut.TryPerform();
var success = sut.TryRevert();
@ -305,7 +444,9 @@ namespace SafeExamBrowser.Core.UnitTests.Behaviour.Operations
operations.Enqueue(operationB.Object);
operations.Enqueue(operationC.Object);
sut.TryPerform(operations);
var sut = new OperationSequence(loggerMock.Object, operations);
sut.TryPerform();
var success = sut.TryRevert();
@ -330,7 +471,9 @@ namespace SafeExamBrowser.Core.UnitTests.Behaviour.Operations
operations.Enqueue(operationB.Object);
operations.Enqueue(operationC.Object);
sut.TryPerform(operations);
var sut = new OperationSequence(loggerMock.Object, operations);
sut.TryPerform();
var success = sut.TryRevert();
@ -342,15 +485,18 @@ namespace SafeExamBrowser.Core.UnitTests.Behaviour.Operations
}
[TestMethod]
public void MustNotFailWithEmptyQueueWhenReverting()
public void MustSucceedWithEmptyQueueWhenReverting()
{
sut.TryPerform(new Queue<IOperation>());
var sut = new OperationSequence(loggerMock.Object, new Queue<IOperation>());
sut.TryPerform();
sut.TryRevert();
}
[TestMethod]
public void MustNotFailWithoutPerformWhenReverting()
public void MustSucceedRevertingWithoutCallingPerform()
{
var sut = new OperationSequence(loggerMock.Object, new Queue<IOperation>());
var success = sut.TryRevert();
Assert.IsTrue(success);
@ -359,8 +505,15 @@ namespace SafeExamBrowser.Core.UnitTests.Behaviour.Operations
[TestMethod]
public void MustNotFailInCaseOfUnexpectedErrorWhenReverting()
{
uiFactoryMock.Setup(l => l.CreateSplashScreen(It.IsAny<IRuntimeInfo>(), It.IsAny<IText>())).Throws(new Exception());
sut.TryRevert();
var sut = new OperationSequence(loggerMock.Object, new Queue<IOperation>());
var indicatorMock = new Mock<IProgressIndicator>();
indicatorMock.Setup(i => i.SetMaxValue(It.IsAny<int>())).Throws<Exception>();
sut.ProgressIndicator = indicatorMock.Object;
var success = sut.TryRevert();
Assert.IsFalse(success);
}
#endregion

View file

@ -23,7 +23,7 @@ namespace SafeExamBrowser.Core.Behaviour.Operations
private IText text;
public bool Abort { get; private set; }
public ISplashScreen SplashScreen { private get; set; }
public IProgressIndicator ProgressIndicator { private get; set; }
public I18nOperation(ILogger logger, IText text)
{

View file

@ -10,8 +10,6 @@ using System;
using System.Collections.Generic;
using System.Linq;
using SafeExamBrowser.Contracts.Behaviour.Operations;
using SafeExamBrowser.Contracts.Configuration;
using SafeExamBrowser.Contracts.I18n;
using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.UserInterface;
@ -20,42 +18,34 @@ namespace SafeExamBrowser.Core.Behaviour.Operations
public class OperationSequence : IOperationSequence
{
private ILogger logger;
private IRuntimeInfo runtimeInfo;
private ISplashScreen splashScreen;
private IText text;
private IUserInterfaceFactory uiFactory;
private Queue<IOperation> operations = new Queue<IOperation>();
private Stack<IOperation> stack = new Stack<IOperation>();
public OperationSequence(ILogger logger, IRuntimeInfo runtimeInfo, IText text, IUserInterfaceFactory uiFactory)
public IProgressIndicator ProgressIndicator { private get; set; }
public OperationSequence(ILogger logger, Queue<IOperation> operations)
{
this.logger = logger;
this.runtimeInfo = runtimeInfo;
this.text = text;
this.uiFactory = uiFactory;
this.operations = new Queue<IOperation>(operations);
}
public bool TryPerform(Queue<IOperation> operations)
public bool TryPerform()
{
var success = false;
try
{
Initialize(operations.Count);
success = Perform(operations);
Initialize();
success = Perform();
if (!success)
{
RevertOperations();
Revert();
}
}
catch (Exception e)
{
logger.Error($"Failed to perform operations!", e);
}
finally
{
Finish();
logger.Error("Failed to perform operations!", e);
}
return success;
@ -63,7 +53,19 @@ namespace SafeExamBrowser.Core.Behaviour.Operations
public bool TryRepeat()
{
throw new NotImplementedException();
var success = false;
try
{
Initialize();
success = Repeat();
}
catch (Exception e)
{
logger.Error("Failed to repeat operations!", e);
}
return success;
}
public bool TryRevert()
@ -73,46 +75,38 @@ namespace SafeExamBrowser.Core.Behaviour.Operations
try
{
Initialize();
success = RevertOperations(false);
success = Revert(false);
}
catch (Exception e)
{
logger.Error($"Failed to revert operations!", e);
}
finally
{
Finish();
logger.Error("Failed to revert operations!", e);
}
return success;
}
private void Initialize(int? operationCount = null)
private void Initialize(bool indeterminate = false)
{
splashScreen = uiFactory.CreateSplashScreen(runtimeInfo, text);
if (operationCount.HasValue)
if (indeterminate)
{
splashScreen.SetMaxProgress(operationCount.Value);
ProgressIndicator?.SetIndeterminate();
}
else
{
splashScreen.SetIndeterminate();
ProgressIndicator?.SetValue(0);
ProgressIndicator?.SetMaxValue(operations.Count);
}
splashScreen.UpdateText(TextKey.SplashScreen_StartupProcedure);
splashScreen.Show();
}
private bool Perform(Queue<IOperation> operations)
private bool Perform()
{
foreach (var operation in operations)
{
stack.Push(operation);
operation.SplashScreen = splashScreen;
try
{
operation.ProgressIndicator = ProgressIndicator;
operation.Perform();
}
catch (Exception e)
@ -127,13 +121,40 @@ namespace SafeExamBrowser.Core.Behaviour.Operations
return false;
}
splashScreen.Progress();
ProgressIndicator?.Progress();
}
return true;
}
private bool RevertOperations(bool regress = true)
private bool Repeat()
{
foreach (var operation in operations)
{
try
{
operation.ProgressIndicator = ProgressIndicator;
operation.Repeat();
}
catch (Exception e)
{
logger.Error($"Failed to repeat operation '{operation.GetType().Name}'!", e);
return false;
}
if (operation.Abort)
{
return false;
}
ProgressIndicator?.Progress();
}
return true;
}
private bool Revert(bool regress = true)
{
var success = true;
@ -143,6 +164,7 @@ namespace SafeExamBrowser.Core.Behaviour.Operations
try
{
operation.ProgressIndicator = ProgressIndicator;
operation.Revert();
}
catch (Exception e)
@ -153,16 +175,11 @@ namespace SafeExamBrowser.Core.Behaviour.Operations
if (regress)
{
splashScreen.Regress();
ProgressIndicator?.Regress();
}
}
return success;
}
private void Finish()
{
splashScreen?.Close();
}
}
}

View file

@ -87,9 +87,6 @@
<Entry key="SplashScreen_StartMouseInterception">
Starting mouse interception
</Entry>
<Entry key="SplashScreen_StartupProcedure">
Initiating startup procedure
</Entry>
<Entry key="SplashScreen_StopEventHandling">
Stopping event handling
</Entry>

View file

@ -26,7 +26,6 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations
private Mock<IRuntimeInfo> info;
private Mock<ISettingsRepository> repository;
private Mock<ISettings> settings;
private Mock<ISplashScreen> splashScreen;
private Mock<IText> text;
private Mock<IUserInterfaceFactory> uiFactory;
private ConfigurationOperation sut;
@ -38,7 +37,6 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations
info = new Mock<IRuntimeInfo>();
repository = new Mock<ISettingsRepository>();
settings = new Mock<ISettings>();
splashScreen = new Mock<ISplashScreen>();
text = new Mock<IText>();
uiFactory = new Mock<IUserInterfaceFactory>();
@ -54,17 +52,11 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations
{
repository.Setup(r => r.LoadDefaults());
sut = new ConfigurationOperation(logger.Object, info.Object, repository.Object, text.Object, uiFactory.Object, null)
{
SplashScreen = splashScreen.Object
};
sut = new ConfigurationOperation(logger.Object, info.Object, repository.Object, text.Object, uiFactory.Object, null);
sut.Perform();
sut = new ConfigurationOperation(logger.Object, info.Object, repository.Object, text.Object, uiFactory.Object, new string[] { })
{
SplashScreen = splashScreen.Object
};
sut = new ConfigurationOperation(logger.Object, info.Object, repository.Object, text.Object, uiFactory.Object, new string[] { });
sut.Perform();
@ -76,10 +68,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations
{
var path = @"an/invalid\path.'*%yolo/()";
sut = new ConfigurationOperation(logger.Object, info.Object, repository.Object, text.Object, uiFactory.Object, new [] { "blubb.exe", path })
{
SplashScreen = splashScreen.Object
};
sut = new ConfigurationOperation(logger.Object, info.Object, repository.Object, text.Object, uiFactory.Object, new [] { "blubb.exe", path });
sut.Perform();
}
@ -93,10 +82,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations
info.SetupGet(r => r.ProgramDataFolder).Returns(location);
info.SetupGet(r => r.AppDataFolder).Returns(location);
sut = new ConfigurationOperation(logger.Object, info.Object, repository.Object, text.Object, uiFactory.Object, new[] { "blubb.exe", path })
{
SplashScreen = splashScreen.Object
};
sut = new ConfigurationOperation(logger.Object, info.Object, repository.Object, text.Object, uiFactory.Object, new[] { "blubb.exe", path });
sut.Perform();
@ -111,10 +97,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations
info.SetupGet(r => r.ProgramDataFolder).Returns(location);
info.SetupGet(r => r.AppDataFolder).Returns($@"{location}\WRONG");
sut = new ConfigurationOperation(logger.Object, info.Object, repository.Object, text.Object, uiFactory.Object, null)
{
SplashScreen = splashScreen.Object
};
sut = new ConfigurationOperation(logger.Object, info.Object, repository.Object, text.Object, uiFactory.Object, null);
sut.Perform();
@ -128,10 +111,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations
info.SetupGet(r => r.AppDataFolder).Returns(location);
sut = new ConfigurationOperation(logger.Object, info.Object, repository.Object, text.Object, uiFactory.Object, null)
{
SplashScreen = splashScreen.Object
};
sut = new ConfigurationOperation(logger.Object, info.Object, repository.Object, text.Object, uiFactory.Object, null);
sut.Perform();
@ -141,10 +121,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations
[TestMethod]
public void MustFallbackToDefaultsAsLastPrio()
{
sut = new ConfigurationOperation(logger.Object, info.Object, repository.Object, text.Object, uiFactory.Object, null)
{
SplashScreen = splashScreen.Object
};
sut = new ConfigurationOperation(logger.Object, info.Object, repository.Object, text.Object, uiFactory.Object, null);
sut.Perform();
@ -159,10 +136,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations
info.SetupGet(r => r.ProgramDataFolder).Returns(location);
uiFactory.Setup(u => u.Show(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<MessageBoxAction>(), It.IsAny<MessageBoxIcon>())).Returns(MessageBoxResult.Yes);
sut = new ConfigurationOperation(logger.Object, info.Object, repository.Object, text.Object, uiFactory.Object, null)
{
SplashScreen = splashScreen.Object
};
sut = new ConfigurationOperation(logger.Object, info.Object, repository.Object, text.Object, uiFactory.Object, null);
sut.Perform();
@ -174,10 +148,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations
{
uiFactory.Setup(u => u.Show(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<MessageBoxAction>(), It.IsAny<MessageBoxIcon>())).Returns(MessageBoxResult.No);
sut = new ConfigurationOperation(logger.Object, info.Object, repository.Object, text.Object, uiFactory.Object, null)
{
SplashScreen = splashScreen.Object
};
sut = new ConfigurationOperation(logger.Object, info.Object, repository.Object, text.Object, uiFactory.Object, null);
sut.Perform();

View file

@ -24,7 +24,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations
private Mock<ILogger> logger;
private Mock<IServiceProxy> service;
private Mock<ISettingsRepository> settings;
private Mock<ISplashScreen> splashScreen;
private Mock<IProgressIndicator> progressIndicator;
private Mock<IText> text;
private ServiceOperation sut;
@ -34,13 +34,10 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations
logger = new Mock<ILogger>();
service = new Mock<IServiceProxy>();
settings = new Mock<ISettingsRepository>();
splashScreen = new Mock<ISplashScreen>();
progressIndicator = new Mock<IProgressIndicator>();
text = new Mock<IText>();
sut = new ServiceOperation(logger.Object, service.Object, settings.Object, text.Object)
{
SplashScreen = splashScreen.Object
};
sut = new ServiceOperation(logger.Object, service.Object, settings.Object, text.Object);
}
[TestMethod]

View file

@ -60,7 +60,7 @@ namespace SafeExamBrowser.Runtime
instances.BuildObjectGraph();
instances.LogStartupInformation();
var success = instances.RuntimeController.TryStart(instances.StartupOperations);
var success = instances.RuntimeController.TryStart();
if (!success)
{

View file

@ -27,7 +27,7 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations
private string[] commandLineArgs;
public bool Abort { get; private set; }
public ISplashScreen SplashScreen { private get; set; }
public IProgressIndicator ProgressIndicator { private get; set; }
public ConfigurationOperation(
ILogger logger,
@ -48,7 +48,7 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations
public void Perform()
{
logger.Info("Initializing application configuration...");
SplashScreen.UpdateText(TextKey.SplashScreen_InitializeConfiguration);
ProgressIndicator?.UpdateText(TextKey.SplashScreen_InitializeConfiguration);
ISettings settings;
var isValidUri = TryGetSettingsUri(out Uri uri);
@ -73,7 +73,7 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations
public void Repeat()
{
// TODO
// TODO: How will the new settings be retrieved? Uri passed to the repository? If yes, how does the Uri get here?!
}
public void Revert()

View file

@ -21,7 +21,7 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations
private KioskMode kioskMode;
public bool Abort { get; private set; }
public ISplashScreen SplashScreen { private get; set; }
public IProgressIndicator ProgressIndicator { private get; set; }
public KioskModeOperation(ILogger logger, ISettingsRepository settingsRepository)
{
@ -34,7 +34,7 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations
kioskMode = settingsRepository.Current.KioskMode;
logger.Info($"Initializing kiosk mode '{kioskMode}'...");
SplashScreen.UpdateText(TextKey.SplashScreen_InitializeKioskMode);
ProgressIndicator?.UpdateText(TextKey.SplashScreen_InitializeKioskMode);
if (kioskMode == KioskMode.CreateNewDesktop)
{
@ -54,7 +54,7 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations
public void Revert()
{
logger.Info($"Reverting kiosk mode '{kioskMode}'...");
SplashScreen.UpdateText(TextKey.SplashScreen_RevertKioskMode);
ProgressIndicator?.UpdateText(TextKey.SplashScreen_RevertKioskMode);
if (kioskMode == KioskMode.CreateNewDesktop)
{

View file

@ -26,7 +26,7 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations
private IText text;
public bool Abort { get; private set; }
public ISplashScreen SplashScreen { private get; set; }
public IProgressIndicator ProgressIndicator { private get; set; }
public ServiceOperation(ILogger logger, IServiceProxy service, ISettingsRepository settingsRepository, IText text)
{
@ -39,7 +39,7 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations
public void Perform()
{
logger.Info($"Initializing service connection...");
SplashScreen.UpdateText(TextKey.SplashScreen_InitializeServiceConnection);
ProgressIndicator?.UpdateText(TextKey.SplashScreen_InitializeServiceConnection);
try
{
@ -71,7 +71,7 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations
public void Revert()
{
logger.Info("Closing service connection...");
SplashScreen.UpdateText(TextKey.SplashScreen_CloseServiceConnection);
ProgressIndicator?.UpdateText(TextKey.SplashScreen_CloseServiceConnection);
if (serviceAvailable)
{

View file

@ -7,7 +7,6 @@
*/
using System;
using System.Collections.Generic;
using SafeExamBrowser.Contracts.Behaviour;
using SafeExamBrowser.Contracts.Behaviour.Operations;
using SafeExamBrowser.Contracts.Communication;
@ -22,18 +21,21 @@ namespace SafeExamBrowser.Runtime.Behaviour
internal class RuntimeController : IRuntimeController
{
private ILogger logger;
private IOperationSequence bootstrapSequence;
private IOperationSequence sessionSequence;
private IRuntimeInfo runtimeInfo;
private IRuntimeWindow runtimeWindow;
private IServiceProxy serviceProxy;
private ISettingsRepository settingsRepository;
private IOperationSequence operationSequence;
private ISplashScreen splashScreen;
private Action terminationCallback;
private IText text;
private IUserInterfaceFactory uiFactory;
public RuntimeController(
ILogger logger,
IOperationSequence operationSequence,
IOperationSequence bootstrapSequence,
IOperationSequence sessionSequence,
IRuntimeInfo runtimeInfo,
IServiceProxy serviceProxy,
ISettingsRepository settingsRepository,
@ -42,7 +44,8 @@ namespace SafeExamBrowser.Runtime.Behaviour
IUserInterfaceFactory uiFactory)
{
this.logger = logger;
this.operationSequence = operationSequence;
this.bootstrapSequence = bootstrapSequence;
this.sessionSequence = sessionSequence;
this.runtimeInfo = runtimeInfo;
this.serviceProxy = serviceProxy;
this.settingsRepository = settingsRepository;
@ -51,21 +54,30 @@ namespace SafeExamBrowser.Runtime.Behaviour
this.uiFactory = uiFactory;
}
public bool TryStart(Queue<IOperation> operations)
public bool TryStart()
{
logger.Info("--- Initiating startup procedure ---");
var success = operationSequence.TryPerform(operations);
splashScreen = uiFactory.CreateSplashScreen(runtimeInfo, text);
splashScreen.Show();
runtimeWindow = uiFactory.CreateRuntimeWindow(runtimeInfo, text);
bootstrapSequence.ProgressIndicator = splashScreen;
var success = bootstrapSequence.TryPerform();
System.Threading.Thread.Sleep(5000);
if (success)
{
runtimeWindow = uiFactory.CreateRuntimeWindow(runtimeInfo, text);
logger.Info("--- Application successfully initialized! ---");
logger.Log(string.Empty);
logger.Subscribe(runtimeWindow);
StartSession();
splashScreen.Hide();
StartSession(true);
}
else
{
@ -76,33 +88,6 @@ namespace SafeExamBrowser.Runtime.Behaviour
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();
}
terminationCallback.Invoke();
}
public void Terminate()
{
StopSession();
@ -113,12 +98,13 @@ namespace SafeExamBrowser.Runtime.Behaviour
// - Revert kiosk mode (or do that when stopping session?)
logger.Unsubscribe(runtimeWindow);
runtimeWindow.Close();
runtimeWindow?.Close();
splashScreen?.Show();
logger.Log(string.Empty);
logger.Info("--- Initiating shutdown procedure ---");
var success = operationSequence.TryRevert();
var success = bootstrapSequence.TryRevert();
if (success)
{
@ -128,6 +114,48 @@ namespace SafeExamBrowser.Runtime.Behaviour
{
logger.Info("--- Shutdown procedure failed! ---");
}
splashScreen?.Close();
}
private void StartSession(bool initial = false)
{
logger.Info("Starting new session...");
runtimeWindow.UpdateText(TextKey.RuntimeWindow_StartSession, true);
runtimeWindow.Show();
sessionSequence.ProgressIndicator = runtimeWindow;
// TODO:
// - Initialize configuration
// - Initialize kiosk mode
// - Initialize session data
// - Create and connect to client
// - Initialize session with service
// - Verify session integrity and start event handling
var success = initial ? sessionSequence.TryPerform() : sessionSequence.TryRepeat();
if (success)
{
}
else
{
}
System.Threading.Thread.Sleep(5000);
runtimeWindow.UpdateText(TextKey.RuntimeWindow_ApplicationRunning);
if (settingsRepository.Current.KioskMode == KioskMode.DisableExplorerShell)
{
runtimeWindow.Hide();
}
System.Threading.Thread.Sleep(5000);
terminationCallback.Invoke();
}
private void StopSession()
@ -135,7 +163,7 @@ namespace SafeExamBrowser.Runtime.Behaviour
logger.Info("Stopping current session...");
runtimeWindow.Show();
runtimeWindow.BringToForeground();
runtimeWindow.UpdateStatus(TextKey.RuntimeWindow_StopSession, true);
runtimeWindow.UpdateText(TextKey.RuntimeWindow_StopSession, true);
// TODO:
// - Terminate client (or does it terminate itself?)

View file

@ -35,11 +35,12 @@ namespace SafeExamBrowser.Runtime
private ISystemInfo systemInfo;
internal IRuntimeController RuntimeController { get; private set; }
internal Queue<IOperation> StartupOperations { get; private set; }
internal void BuildObjectGraph()
{
var args = Environment.GetCommandLineArgs();
var bootstrapOperations = new Queue<IOperation>();
var sessionOperations = new Queue<IOperation>();
var nativeMethods = new NativeMethods();
var settingsRepository = new SettingsRepository();
var uiFactory = new UserInterfaceFactory();
@ -52,16 +53,19 @@ namespace SafeExamBrowser.Runtime
InitializeLogging();
var text = new Text(logger);
var operationSequence = new OperationSequence(logger, runtimeInfo, text, uiFactory);
var serviceProxy = new ServiceProxy(new ModuleLogger(logger, typeof(ServiceProxy)), "net.pipe://localhost/safeexambrowser/service");
RuntimeController = new RuntimeController(logger, operationSequence, runtimeInfo, serviceProxy, settingsRepository, Application.Current.Shutdown, text, uiFactory);
bootstrapOperations.Enqueue(new I18nOperation(logger, text));
// TODO: RuntimeHostOperation here (is IBootstrapOperation -> only performed once per runtime!)
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(logger, settingsRepository));
sessionOperations.Enqueue(new ConfigurationOperation(logger, runtimeInfo, settingsRepository, text, uiFactory, args));
sessionOperations.Enqueue(new ServiceOperation(logger, serviceProxy, settingsRepository, text));
sessionOperations.Enqueue(new KioskModeOperation(logger, settingsRepository));
var bootstrapSequence = new OperationSequence(logger, bootstrapOperations);
var sessionSequence = new OperationSequence(logger, sessionOperations);
RuntimeController = new RuntimeController(logger, bootstrapSequence, sessionSequence, runtimeInfo, serviceProxy, settingsRepository, Application.Current.Shutdown, text, uiFactory);
}
internal void LogStartupInformation()

View file

@ -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 x:Name="AnimatedBorder" Panel.ZIndex="10" BorderBrush="DodgerBlue" BorderThickness="5">
<Border x:Name="AnimatedBorder" Panel.ZIndex="10" BorderBrush="DodgerBlue" BorderThickness="5" Visibility="{Binding AnimatedBorderVisibility}">
<Border.Effect>
<BlurEffect Radius="10" />
</Border.Effect>
@ -41,8 +41,11 @@
<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" 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" />
<ProgressBar x:Name="ProgressBar" Grid.Row="1" Background="WhiteSmoke" BorderThickness="0" Foreground="DodgerBlue"
IsIndeterminate="{Binding IsIndeterminate}" Maximum="{Binding MaxProgress}" Value="{Binding CurrentProgress}"
Visibility="{Binding ProgressBarVisibility}" />
<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>

View file

@ -9,7 +9,6 @@
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;
@ -62,6 +61,11 @@ namespace SafeExamBrowser.UserInterface.Classic
Dispatcher.Invoke(base.Hide);
}
public void HideProgressBar()
{
model.ProgressBarVisibility = Visibility.Hidden;
}
public void Notify(ILogContent content)
{
Dispatcher.Invoke(() =>
@ -71,28 +75,52 @@ namespace SafeExamBrowser.UserInterface.Classic
});
}
public void Progress(int amount = 1)
{
model.CurrentProgress += amount;
}
public void Regress(int amount = 1)
{
model.CurrentProgress -= amount;
}
public void SetIndeterminate()
{
model.IsIndeterminate = true;
}
public void SetMaxValue(int max)
{
model.MaxProgress = max;
}
public void SetValue(int value)
{
model.CurrentProgress = value;
}
public void ShowProgressBar()
{
model.ProgressBarVisibility = Visibility.Visible;
}
public void UpdateText(TextKey key, bool showBusyIndication = false)
{
model.StopBusyIndication();
model.Status = text.Get(key);
if (showBusyIndication)
{
model.StartBusyIndication();
}
}
public new void Show()
{
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}";
@ -103,6 +131,8 @@ namespace SafeExamBrowser.UserInterface.Classic
InfoTextBlock.Inlines.Add(new Run(runtimeInfo.ProgramCopyright) { FontSize = 10 });
model = new RuntimeWindowViewModel();
AnimatedBorder.DataContext = model;
ProgressBar.DataContext = model;
StatusTextBlock.DataContext = model;
Closing += (o, args) => args.Cancel = !allowClose;

View file

@ -118,8 +118,8 @@
<Compile Include="Utilities\XamlIconResource.cs" />
<Compile Include="ViewModels\DateTimeViewModel.cs" />
<Compile Include="ViewModels\LogViewModel.cs" />
<Compile Include="ViewModels\ProgressIndicatorViewModel.cs" />
<Compile Include="ViewModels\RuntimeWindowViewModel.cs" />
<Compile Include="ViewModels\SplashScreenViewModel.cs" />
<Page Include="AboutWindow.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>

View file

@ -18,7 +18,7 @@ namespace SafeExamBrowser.UserInterface.Classic
public partial class SplashScreen : Window, ISplashScreen
{
private bool allowClose;
private SplashScreenViewModel model = new SplashScreenViewModel();
private ProgressIndicatorViewModel model = new ProgressIndicatorViewModel();
private IRuntimeInfo runtimeInfo;
private IText text;
private WindowClosingEventHandler closing;
@ -77,11 +77,16 @@ namespace SafeExamBrowser.UserInterface.Classic
model.IsIndeterminate = true;
}
public void SetMaxProgress(int max)
public void SetMaxValue(int max)
{
model.MaxProgress = max;
}
public void SetValue(int value)
{
model.CurrentProgress = value;
}
public void UpdateText(TextKey key, bool showBusyIndication = false)
{
model.StopBusyIndication();

View file

@ -11,7 +11,7 @@ using System.Timers;
namespace SafeExamBrowser.UserInterface.Classic.ViewModels
{
class SplashScreenViewModel : INotifyPropertyChanged
internal class ProgressIndicatorViewModel : INotifyPropertyChanged
{
private int currentProgress;
private bool isIndeterminate;
@ -73,7 +73,7 @@ namespace SafeExamBrowser.UserInterface.Classic.ViewModels
}
}
public void StartBusyIndication()
public virtual void StartBusyIndication()
{
StopBusyIndication();
@ -87,12 +87,17 @@ namespace SafeExamBrowser.UserInterface.Classic.ViewModels
busyTimer.Start();
}
public void StopBusyIndication()
public virtual void StopBusyIndication()
{
busyTimer?.Stop();
busyTimer?.Close();
}
protected void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private void BusyTimer_Elapsed(object sender, ElapsedEventArgs e)
{
var next = Status ?? string.Empty;

View file

@ -6,65 +6,52 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
using System.ComponentModel;
using System.Timers;
using System.Windows;
namespace SafeExamBrowser.UserInterface.Classic.ViewModels
{
internal class RuntimeWindowViewModel : INotifyPropertyChanged
internal class RuntimeWindowViewModel : ProgressIndicatorViewModel
{
private string status;
private Timer timer;
private Visibility animatedBorderVisibility, progressBarVisibility;
public event PropertyChangedEventHandler PropertyChanged;
public string Status
public Visibility AnimatedBorderVisibility
{
get
{
return status;
return animatedBorderVisibility;
}
set
{
status = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Status)));
animatedBorderVisibility = value;
OnPropertyChanged(nameof(AnimatedBorderVisibility));
}
}
public void StartBusyIndication()
public Visibility ProgressBarVisibility
{
StopBusyIndication();
timer = new Timer
get
{
AutoReset = true,
Interval = 750
};
timer.Elapsed += Timer_Elapsed;
timer.Start();
return progressBarVisibility;
}
set
{
progressBarVisibility = value;
OnPropertyChanged(nameof(ProgressBarVisibility));
}
}
public void StopBusyIndication()
public override void StartBusyIndication()
{
timer?.Stop();
timer?.Close();
base.StartBusyIndication();
AnimatedBorderVisibility = Visibility.Hidden;
}
private void Timer_Elapsed(object sender, ElapsedEventArgs e)
public override void StopBusyIndication()
{
var next = Status ?? string.Empty;
base.StopBusyIndication();
if (next.EndsWith("..."))
{
next = Status.Substring(0, Status.Length - 3);
}
else
{
next += ".";
}
Status = next;
AnimatedBorderVisibility = Visibility.Visible;
}
}
}

View file

@ -77,11 +77,16 @@ namespace SafeExamBrowser.UserInterface.Windows10
model.IsIndeterminate = true;
}
public void SetMaxProgress(int max)
public void SetMaxValue(int max)
{
model.MaxProgress = max;
}
public void SetValue(int value)
{
model.CurrentProgress = value;
}
public void UpdateText(TextKey key, bool showBusyIndication = false)
{
model.StopBusyIndication();