SEBWIN-141: Extended system component contracts and implementations to allow multiple controls per component and started to implement action center functionality.

This commit is contained in:
dbuechel 2019-03-08 15:56:38 +01:00
parent 519fb9e57b
commit 31857bfb25
26 changed files with 579 additions and 476 deletions

View file

@ -44,16 +44,11 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
[TestMethod]
public void MustPeformCorrectly()
{
var order = 0;
controller.Setup(c => c.Initialize()).Callback(() => Assert.AreEqual(++order, 1));
controller.Setup(c => c.RegisterApplicationControl(It.IsAny<IApplicationControl>())).Callback(() => Assert.AreEqual(++order, 2));
taskbar.Setup(t => t.AddApplicationControl(It.IsAny<IApplicationControl>())).Callback(() => Assert.AreEqual(++order, 3));
sut.Perform();
controller.Verify(c => c.Initialize(), Times.Once);
controller.Verify(c => c.RegisterApplicationControl(It.IsAny<IApplicationControl>()), Times.Once);
controller.Verify(c => c.RegisterApplicationControl(It.IsAny<IApplicationControl>()), Times.Exactly(2));
actionCenter.Verify(a => a.AddApplicationControl(It.IsAny<IApplicationControl>()), Times.Once);
taskbar.Verify(t => t.AddApplicationControl(It.IsAny<IApplicationControl>()), Times.Once);
}
@ -61,7 +56,6 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
public void MustRevertCorrectly()
{
sut.Revert();
controller.Verify(c => c.Terminate(), Times.Once);
}
}

View file

@ -6,6 +6,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using SafeExamBrowser.Client.Operations;
@ -19,10 +20,13 @@ using SafeExamBrowser.Contracts.UserInterface.Shell;
namespace SafeExamBrowser.Client.UnitTests.Operations
{
[TestClass]
public class TaskbarOperationTests
public class ShellOperationTests
{
private Mock<IActionCenter> actionCenter;
private Mock<IEnumerable<IActionCenterActivator>> activators;
private ActionCenterSettings actionCenterSettings;
private Mock<ILogger> loggerMock;
private TaskbarSettings settings;
private TaskbarSettings taskbarSettings;
private Mock<INotificationInfo> aboutInfoMock;
private Mock<INotificationController> aboutControllerMock;
private Mock<INotificationInfo> logInfoMock;
@ -34,11 +38,14 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
private Mock<ITaskbar> taskbarMock;
private Mock<IUserInterfaceFactory> uiFactoryMock;
private TaskbarOperation sut;
private ShellOperation sut;
[TestInitialize]
public void Initialize()
{
actionCenter = new Mock<IActionCenter>();
activators = new Mock<IEnumerable<IActionCenterActivator>>();
actionCenterSettings = new ActionCenterSettings();
loggerMock = new Mock<ILogger>();
aboutInfoMock = new Mock<INotificationInfo>();
aboutControllerMock = new Mock<INotificationController>();
@ -49,17 +56,20 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
wirelessNetworkMock = new Mock<ISystemComponent<ISystemWirelessNetworkControl>>();
systemInfoMock = new Mock<ISystemInfo>();
taskbarMock = new Mock<ITaskbar>();
settings = new TaskbarSettings();
taskbarSettings = new TaskbarSettings();
uiFactoryMock = new Mock<IUserInterfaceFactory>();
settings.AllowApplicationLog = true;
settings.AllowKeyboardLayout = true;
settings.AllowWirelessNetwork = true;
settings.EnableTaskbar = true;
taskbarSettings.AllowApplicationLog = true;
taskbarSettings.AllowKeyboardLayout = true;
taskbarSettings.AllowWirelessNetwork = true;
taskbarSettings.EnableTaskbar = true;
systemInfoMock.SetupGet(s => s.HasBattery).Returns(true);
uiFactoryMock.Setup(u => u.CreateNotificationControl(It.IsAny<INotificationInfo>())).Returns(new Mock<INotificationControl>().Object);
uiFactoryMock.Setup(u => u.CreateNotificationControl(It.IsAny<INotificationInfo>(), It.IsAny<Location>())).Returns(new Mock<INotificationControl>().Object);
sut = new TaskbarOperation(
sut = new ShellOperation(
actionCenter.Object,
activators.Object,
actionCenterSettings,
loggerMock.Object,
aboutInfoMock.Object,
aboutControllerMock.Object,
@ -70,7 +80,7 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
wirelessNetworkMock.Object,
systemInfoMock.Object,
taskbarMock.Object,
settings,
taskbarSettings,
uiFactoryMock.Object);
}
@ -79,9 +89,9 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
{
sut.Perform();
keyboardLayoutMock.Verify(k => k.Initialize(It.IsAny<ISystemKeyboardLayoutControl>()), Times.Once);
powerSupplyMock.Verify(p => p.Initialize(It.IsAny<ISystemPowerSupplyControl>()), Times.Once);
wirelessNetworkMock.Verify(w => w.Initialize(It.IsAny<ISystemWirelessNetworkControl>()), Times.Once);
keyboardLayoutMock.Verify(k => k.Initialize(), Times.Once);
powerSupplyMock.Verify(p => p.Initialize(), Times.Once);
wirelessNetworkMock.Verify(w => w.Initialize(), Times.Once);
taskbarMock.Verify(t => t.AddSystemControl(It.IsAny<ISystemControl>()), Times.Exactly(3));
taskbarMock.Verify(t => t.AddNotificationControl(It.IsAny<INotificationControl>()), Times.Exactly(2));
}

View file

@ -90,7 +90,7 @@
<Compile Include="Operations\MouseInterceptorOperationTests.cs" />
<Compile Include="Operations\ProcessMonitorOperationTests.cs" />
<Compile Include="Operations\RuntimeConnectionOperationTests.cs" />
<Compile Include="Operations\TaskbarOperationTests.cs" />
<Compile Include="Operations\ShellOperationTests.cs" />
<Compile Include="Operations\WindowMonitorOperationTests.cs" />
<Compile Include="Communication\ClientHostTests.cs" />
<Compile Include="Notifications\AboutNotificationControllerTests.cs" />

View file

@ -116,8 +116,7 @@ namespace SafeExamBrowser.Client
operations.Enqueue(new LazyInitializationOperation(BuildWindowMonitorOperation));
operations.Enqueue(new LazyInitializationOperation(BuildProcessMonitorOperation));
operations.Enqueue(new DisplayMonitorOperation(displayMonitor, logger, taskbar));
operations.Enqueue(new LazyInitializationOperation(BuildActionCenterOperation));
operations.Enqueue(new LazyInitializationOperation(BuildTaskbarOperation));
operations.Enqueue(new LazyInitializationOperation(BuildShellOperation));
operations.Enqueue(new LazyInitializationOperation(BuildBrowserOperation));
operations.Enqueue(new ClipboardOperation(logger, nativeMethods));
operations.Enqueue(new DelegateOperation(UpdateClientControllerDependencies));
@ -196,35 +195,6 @@ namespace SafeExamBrowser.Client
textResource = new XmlTextResource(path);
}
private IOperation BuildActionCenterOperation()
{
var aboutInfo = new AboutNotificationInfo(text);
var aboutController = new AboutNotificationController(configuration.AppConfig, uiFactory);
var logInfo = new LogNotificationInfo(text);
var logController = new LogNotificationController(logger, uiFactory);
var activators = new IActionCenterActivator[]
{
new KeyboardActivator(new ModuleLogger(logger, nameof(KeyboardActivator))),
new TouchActivator(new ModuleLogger(logger, nameof(TouchActivator)))
};
var operation = new ActionCenterOperation(
actionCenter,
activators,
logger,
aboutInfo,
aboutController,
logInfo,
logController,
keyboardLayout,
powerSupply,
wirelessNetwork,
configuration.Settings.ActionCenter,
systemInfo,
uiFactory);
return operation;
}
private IOperation BuildBrowserOperation()
{
var moduleLogger = new ModuleLogger(logger, "BrowserController");
@ -280,13 +250,21 @@ namespace SafeExamBrowser.Client
return new ProcessMonitorOperation(logger, processMonitor, configuration.Settings);
}
private IOperation BuildTaskbarOperation()
private IOperation BuildShellOperation()
{
var aboutInfo = new AboutNotificationInfo(text);
var aboutController = new AboutNotificationController(configuration.AppConfig, uiFactory);
var logInfo = new LogNotificationInfo(text);
var logController = new LogNotificationController(logger, uiFactory);
var operation = new TaskbarOperation(
var activators = new IActionCenterActivator[]
{
new KeyboardActivator(new ModuleLogger(logger, nameof(KeyboardActivator))),
new TouchActivator(new ModuleLogger(logger, nameof(TouchActivator)))
};
var operation = new ShellOperation(
actionCenter,
activators,
configuration.Settings.ActionCenter,
logger,
aboutInfo,
aboutController,

View file

@ -1,114 +0,0 @@
/*
* Copyright (c) 2019 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.Collections.Generic;
using SafeExamBrowser.Contracts.Client;
using SafeExamBrowser.Contracts.Configuration.Settings;
using SafeExamBrowser.Contracts.Core.OperationModel;
using SafeExamBrowser.Contracts.Core.OperationModel.Events;
using SafeExamBrowser.Contracts.I18n;
using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.SystemComponents;
using SafeExamBrowser.Contracts.UserInterface;
using SafeExamBrowser.Contracts.UserInterface.Shell;
namespace SafeExamBrowser.Client.Operations
{
internal class ActionCenterOperation : IOperation
{
private IActionCenter actionCenter;
private IEnumerable<IActionCenterActivator> activators;
private ILogger logger;
private INotificationInfo aboutInfo;
private INotificationController aboutController;
private INotificationInfo logInfo;
private INotificationController logController;
private ISystemComponent<ISystemKeyboardLayoutControl> keyboardLayout;
private ISystemComponent<ISystemPowerSupplyControl> powerSupply;
private ISystemComponent<ISystemWirelessNetworkControl> wirelessNetwork;
private ActionCenterSettings settings;
private ISystemInfo systemInfo;
private IUserInterfaceFactory uiFactory;
public event ActionRequiredEventHandler ActionRequired { add { } remove { } }
public event StatusChangedEventHandler StatusChanged;
public ActionCenterOperation(
IActionCenter actionCenter,
IEnumerable<IActionCenterActivator> activators,
ILogger logger,
INotificationInfo aboutInfo,
INotificationController aboutController,
INotificationInfo logInfo,
INotificationController logController,
ISystemComponent<ISystemKeyboardLayoutControl> keyboardLayout,
ISystemComponent<ISystemPowerSupplyControl> powerSupply,
ISystemComponent<ISystemWirelessNetworkControl> wirelessNetwork,
ActionCenterSettings settings,
ISystemInfo systemInfo,
IUserInterfaceFactory uiFactory)
{
this.actionCenter = actionCenter;
this.activators = activators;
this.logger = logger;
this.aboutInfo = aboutInfo;
this.aboutController = aboutController;
this.logInfo = logInfo;
this.logController = logController;
this.keyboardLayout = keyboardLayout;
this.powerSupply = powerSupply;
this.wirelessNetwork = wirelessNetwork;
this.systemInfo = systemInfo;
this.settings = settings;
this.uiFactory = uiFactory;
}
public OperationResult Perform()
{
StatusChanged?.Invoke(TextKey.OperationStatus_InitializeActionCenter);
if (settings.EnableActionCenter)
{
logger.Info("Initializing action center...");
foreach (var activator in activators)
{
actionCenter.Register(activator);
activator.Start();
}
}
else
{
logger.Info("Action center is disabled, skipping initialization.");
}
return OperationResult.Success;
}
public OperationResult Revert()
{
StatusChanged?.Invoke(TextKey.OperationStatus_TerminateActionCenter);
if (settings.EnableActionCenter)
{
logger.Info("Terminating action center...");
foreach (var activator in activators)
{
activator.Stop();
}
}
else
{
logger.Info("Action center was disabled, skipping termination.");
}
return OperationResult.Success;
}
}
}

View file

@ -0,0 +1,275 @@
/*
* Copyright (c) 2019 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.Collections.Generic;
using SafeExamBrowser.Contracts.Client;
using SafeExamBrowser.Contracts.Configuration.Settings;
using SafeExamBrowser.Contracts.Core.OperationModel;
using SafeExamBrowser.Contracts.Core.OperationModel.Events;
using SafeExamBrowser.Contracts.I18n;
using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.SystemComponents;
using SafeExamBrowser.Contracts.UserInterface;
using SafeExamBrowser.Contracts.UserInterface.Shell;
namespace SafeExamBrowser.Client.Operations
{
internal class ShellOperation : IOperation
{
private IActionCenter actionCenter;
private IEnumerable<IActionCenterActivator> activators;
private ActionCenterSettings actionCenterSettings;
private ILogger logger;
private INotificationInfo aboutInfo;
private INotificationController aboutController;
private INotificationInfo logInfo;
private INotificationController logController;
private ISystemComponent<ISystemKeyboardLayoutControl> keyboardLayout;
private ISystemComponent<ISystemPowerSupplyControl> powerSupply;
private ISystemComponent<ISystemWirelessNetworkControl> wirelessNetwork;
private ISystemInfo systemInfo;
private ITaskbar taskbar;
private TaskbarSettings taskbarSettings;
private IUserInterfaceFactory uiFactory;
public event ActionRequiredEventHandler ActionRequired { add { } remove { } }
public event StatusChangedEventHandler StatusChanged;
public ShellOperation(
IActionCenter actionCenter,
IEnumerable<IActionCenterActivator> activators,
ActionCenterSettings actionCenterSettings,
ILogger logger,
INotificationInfo aboutInfo,
INotificationController aboutController,
INotificationInfo logInfo,
INotificationController logController,
ISystemComponent<ISystemKeyboardLayoutControl> keyboardLayout,
ISystemComponent<ISystemPowerSupplyControl> powerSupply,
ISystemComponent<ISystemWirelessNetworkControl> wirelessNetwork,
ISystemInfo systemInfo,
ITaskbar taskbar,
TaskbarSettings taskbarSettings,
IUserInterfaceFactory uiFactory)
{
this.aboutInfo = aboutInfo;
this.aboutController = aboutController;
this.actionCenter = actionCenter;
this.activators = activators;
this.actionCenterSettings = actionCenterSettings;
this.logger = logger;
this.logInfo = logInfo;
this.logController = logController;
this.keyboardLayout = keyboardLayout;
this.powerSupply = powerSupply;
this.taskbarSettings = taskbarSettings;
this.systemInfo = systemInfo;
this.taskbar = taskbar;
this.uiFactory = uiFactory;
this.wirelessNetwork = wirelessNetwork;
}
public OperationResult Perform()
{
logger.Info("Initializing shell...");
StatusChanged?.Invoke(TextKey.OperationStatus_InitializeShell);
InitializeSystemComponents();
InitializeActionCenter();
InitializeTaskbar();
return OperationResult.Success;
}
public OperationResult Revert()
{
logger.Info("Terminating shell...");
StatusChanged?.Invoke(TextKey.OperationStatus_TerminateShell);
TerminateNotifications();
TerminateSystemComponents();
return OperationResult.Success;
}
private void InitializeActionCenter()
{
if (actionCenterSettings.EnableActionCenter)
{
logger.Info("Initializing action center...");
InitializeAboutNotificationForActionCenter();
InitializeClockForActionCenter();
InitializeLogNotificationForActionCenter();
InitializeKeyboardLayoutForActionCenter();
InitializeWirelessNetworkForActionCenter();
InitializePowerSupplyForActionCenter();
//if (settings.AllowKeyboardLayout)
//{
// AddKeyboardLayoutControl();
//}
//if (settings.AllowWirelessNetwork)
//{
// AddWirelessNetworkControl();
//}
//if (systemInfo.HasBattery)
//{
// AddPowerSupplyControl();
//}
foreach (var activator in activators)
{
actionCenter.Register(activator);
activator.Start();
}
}
else
{
logger.Info("Action center is disabled, skipping initialization.");
}
}
private void InitializeTaskbar()
{
if (taskbarSettings.EnableTaskbar)
{
logger.Info("Initializing taskbar...");
InitializeAboutNotificationForTaskbar();
InitializeClockForTaskbar();
InitializeLogNotificationForTaskbar();
InitializeKeyboardLayoutForTaskbar();
InitializeWirelessNetworkForTaskbar();
InitializePowerSupplyForTaskbar();
}
else
{
logger.Info("Taskbar is disabled, skipping initialization.");
}
}
private void InitializeSystemComponents()
{
keyboardLayout.Initialize();
powerSupply.Initialize();
wirelessNetwork.Initialize();
}
private void InitializeAboutNotificationForActionCenter()
{
var notification = uiFactory.CreateNotificationControl(aboutInfo, Location.ActionCenter);
aboutController.RegisterNotification(notification);
actionCenter.AddNotificationControl(notification);
}
private void InitializeAboutNotificationForTaskbar()
{
var notification = uiFactory.CreateNotificationControl(aboutInfo, Location.Taskbar);
aboutController.RegisterNotification(notification);
taskbar.AddNotificationControl(notification);
}
private void InitializeClockForActionCenter()
{
//TODO: actionCenter.ShowClock = settings.ShowClock;
}
private void InitializeClockForTaskbar()
{
taskbar.ShowClock = taskbarSettings.ShowClock;
}
private void InitializeLogNotificationForActionCenter()
{
if (actionCenterSettings.AllowApplicationLog)
{
var notification = uiFactory.CreateNotificationControl(logInfo, Location.ActionCenter);
logController.RegisterNotification(notification);
actionCenter.AddNotificationControl(notification);
}
}
private void InitializeLogNotificationForTaskbar()
{
if (taskbarSettings.AllowApplicationLog)
{
var notification = uiFactory.CreateNotificationControl(logInfo, Location.Taskbar);
logController.RegisterNotification(notification);
taskbar.AddNotificationControl(notification);
}
}
private void InitializeKeyboardLayoutForActionCenter()
{
// TODO
}
private void InitializeKeyboardLayoutForTaskbar()
{
if (taskbarSettings.AllowKeyboardLayout)
{
var control = uiFactory.CreateKeyboardLayoutControl();
keyboardLayout.Register(control);
taskbar.AddSystemControl(control);
}
}
private void InitializePowerSupplyForActionCenter()
{
// TODO
}
private void InitializePowerSupplyForTaskbar()
{
if (systemInfo.HasBattery)
{
var control = uiFactory.CreatePowerSupplyControl();
powerSupply.Register(control);
taskbar.AddSystemControl(control);
}
}
private void InitializeWirelessNetworkForActionCenter()
{
// TODO
}
private void InitializeWirelessNetworkForTaskbar()
{
if (taskbarSettings.AllowWirelessNetwork)
{
var control = uiFactory.CreateWirelessNetworkControl();
wirelessNetwork.Register(control);
taskbar.AddSystemControl(control);
}
}
private void TerminateNotifications()
{
aboutController.Terminate();
logController.Terminate();
}
private void TerminateSystemComponents()
{
keyboardLayout.Terminate();
powerSupply.Terminate();
wirelessNetwork.Terminate();
}
}
}

View file

@ -1,183 +0,0 @@
/*
* Copyright (c) 2019 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.Client;
using SafeExamBrowser.Contracts.Configuration.Settings;
using SafeExamBrowser.Contracts.Core.OperationModel;
using SafeExamBrowser.Contracts.Core.OperationModel.Events;
using SafeExamBrowser.Contracts.I18n;
using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.SystemComponents;
using SafeExamBrowser.Contracts.UserInterface;
using SafeExamBrowser.Contracts.UserInterface.Shell;
namespace SafeExamBrowser.Client.Operations
{
internal class TaskbarOperation : IOperation
{
private ILogger logger;
private INotificationInfo aboutInfo;
private INotificationController aboutController;
private INotificationInfo logInfo;
private INotificationController logController;
private TaskbarSettings settings;
private ISystemComponent<ISystemKeyboardLayoutControl> keyboardLayout;
private ISystemComponent<ISystemPowerSupplyControl> powerSupply;
private ISystemComponent<ISystemWirelessNetworkControl> wirelessNetwork;
private ISystemInfo systemInfo;
private ITaskbar taskbar;
private IUserInterfaceFactory uiFactory;
public event ActionRequiredEventHandler ActionRequired { add { } remove { } }
public event StatusChangedEventHandler StatusChanged;
public TaskbarOperation(
ILogger logger,
INotificationInfo aboutInfo,
INotificationController aboutController,
INotificationInfo logInfo,
INotificationController logController,
ISystemComponent<ISystemKeyboardLayoutControl> keyboardLayout,
ISystemComponent<ISystemPowerSupplyControl> powerSupply,
ISystemComponent<ISystemWirelessNetworkControl> wirelessNetwork,
ISystemInfo systemInfo,
ITaskbar taskbar,
TaskbarSettings settings,
IUserInterfaceFactory uiFactory)
{
this.aboutInfo = aboutInfo;
this.aboutController = aboutController;
this.logger = logger;
this.logInfo = logInfo;
this.logController = logController;
this.keyboardLayout = keyboardLayout;
this.powerSupply = powerSupply;
this.settings = settings;
this.systemInfo = systemInfo;
this.taskbar = taskbar;
this.uiFactory = uiFactory;
this.wirelessNetwork = wirelessNetwork;
}
public OperationResult Perform()
{
StatusChanged?.Invoke(TextKey.OperationStatus_InitializeTaskbar);
if (settings.EnableTaskbar)
{
logger.Info("Initializing taskbar...");
AddAboutNotification();
taskbar.ShowClock = settings.ShowClock;
if (settings.AllowApplicationLog)
{
AddLogNotification();
}
if (settings.AllowKeyboardLayout)
{
AddKeyboardLayoutControl();
}
if (settings.AllowWirelessNetwork)
{
AddWirelessNetworkControl();
}
if (systemInfo.HasBattery)
{
AddPowerSupplyControl();
}
}
else
{
logger.Info("Taskbar is disabled, skipping initialization.");
}
return OperationResult.Success;
}
public OperationResult Revert()
{
StatusChanged?.Invoke(TextKey.OperationStatus_TerminateTaskbar);
if (settings.EnableTaskbar)
{
logger.Info("Terminating taskbar...");
aboutController.Terminate();
if (settings.AllowApplicationLog)
{
logController.Terminate();
}
if (settings.AllowKeyboardLayout)
{
keyboardLayout.Terminate();
}
if (settings.AllowWirelessNetwork)
{
wirelessNetwork.Terminate();
}
if (systemInfo.HasBattery)
{
powerSupply.Terminate();
}
}
else
{
logger.Info("Taskbar was disabled, skipping termination.");
}
return OperationResult.Success;
}
private void AddAboutNotification()
{
var aboutNotification = uiFactory.CreateNotificationControl(aboutInfo);
aboutController.RegisterNotification(aboutNotification);
taskbar.AddNotificationControl(aboutNotification);
}
private void AddKeyboardLayoutControl()
{
var control = uiFactory.CreateKeyboardLayoutControl();
keyboardLayout.Initialize(control);
taskbar.AddSystemControl(control);
}
private void AddLogNotification()
{
var logNotification = uiFactory.CreateNotificationControl(logInfo);
logController.RegisterNotification(logNotification);
taskbar.AddNotificationControl(logNotification);
}
private void AddPowerSupplyControl()
{
var control = uiFactory.CreatePowerSupplyControl();
powerSupply.Initialize(control);
taskbar.AddSystemControl(control);
}
private void AddWirelessNetworkControl()
{
var control = uiFactory.CreateWirelessNetworkControl();
wirelessNetwork.Initialize(control);
taskbar.AddSystemControl(control);
}
}
}

View file

@ -72,7 +72,6 @@
<ItemGroup>
<Compile Include="App.cs" />
<Compile Include="ClientController.cs" />
<Compile Include="Operations\ActionCenterOperation.cs" />
<Compile Include="Operations\ClientHostDisconnectionOperation.cs" />
<Compile Include="Operations\ConfigurationOperation.cs" />
<Compile Include="Operations\InitializationOperation.cs" />
@ -91,7 +90,7 @@
<Compile Include="Operations\KeyboardInterceptorOperation.cs" />
<Compile Include="Operations\MouseInterceptorOperation.cs" />
<Compile Include="Operations\ProcessMonitorOperation.cs" />
<Compile Include="Operations\TaskbarOperation.cs" />
<Compile Include="Operations\ShellOperation.cs" />
<Compile Include="Operations\WindowMonitorOperation.cs" />
<Compile Include="Properties\AssemblyInfo.cs">
<SubType>Code</SubType>

View file

@ -145,6 +145,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
settings.Taskbar.ShowClock = true;
// TODO: Default values for testing of alpha version only, remove for final release!
settings.ActionCenter.AllowApplicationLog = true;
settings.Browser.AllowDeveloperConsole = true;
settings.Browser.MainWindowSettings.AllowAddressBar = true;
settings.Taskbar.AllowApplicationLog = true;

View file

@ -16,6 +16,11 @@ namespace SafeExamBrowser.Contracts.Configuration.Settings
[Serializable]
public class ActionCenterSettings
{
/// <summary>
/// Determines whether the user may access the application log during runtime.
/// </summary>
public bool AllowApplicationLog { get; set; }
/// <summary>
/// Determines whether the action center itself is enabled and visible to the user.
/// </summary>

View file

@ -56,7 +56,6 @@ namespace SafeExamBrowser.Contracts.I18n
OperationStatus_CloseRuntimeConnection,
OperationStatus_EmptyClipboard,
OperationStatus_FinalizeServiceSession,
OperationStatus_InitializeActionCenter,
OperationStatus_InitializeBrowser,
OperationStatus_InitializeClient,
OperationStatus_InitializeConfiguration,
@ -64,7 +63,7 @@ namespace SafeExamBrowser.Contracts.I18n
OperationStatus_InitializeProcessMonitoring,
OperationStatus_InitializeRuntimeConnection,
OperationStatus_InitializeServiceSession,
OperationStatus_InitializeTaskbar,
OperationStatus_InitializeShell,
OperationStatus_InitializeWindowMonitoring,
OperationStatus_InitializeWorkingArea,
OperationStatus_RestartCommunicationHost,
@ -81,9 +80,8 @@ namespace SafeExamBrowser.Contracts.I18n
OperationStatus_StopMouseInterception,
OperationStatus_StopProcessMonitoring,
OperationStatus_StopWindowMonitoring,
OperationStatus_TerminateActionCenter,
OperationStatus_TerminateBrowser,
OperationStatus_TerminateTaskbar,
OperationStatus_TerminateShell,
OperationStatus_WaitExplorerStartup,
OperationStatus_WaitExplorerTermination,
OperationStatus_WaitRuntimeDisconnection,

View file

@ -17,9 +17,14 @@ namespace SafeExamBrowser.Contracts.SystemComponents
public interface ISystemComponent<TControl> where TControl : ISystemControl
{
/// <summary>
/// Initializes the resources and operations of the component and registers its taskbar control.
/// Initializes the resources and operations of the component.
/// </summary>
void Initialize(TControl control);
void Initialize();
/// <summary>
/// Registers a system control which will be loaded into shell.
/// </summary>
void Register(TControl control);
/// <summary>
/// Instructs the component to stop any running operations and releases all used resources.

View file

@ -51,9 +51,9 @@ namespace SafeExamBrowser.Contracts.UserInterface
IWindow CreateLogWindow(ILogger logger);
/// <summary>
/// Creates a notification control, initialized with the given notification information.
/// Creates a notification control for the specified location, initialized with the given notification information.
/// </summary>
INotificationControl CreateNotificationControl(INotificationInfo info);
INotificationControl CreateNotificationControl(INotificationInfo info, Location location);
/// <summary>
/// Creates a password dialog with the given message and title.

View file

@ -9,12 +9,12 @@
namespace SafeExamBrowser.Contracts.UserInterface.Shell
{
/// <summary>
/// The control of a system component which can be loaded into the <see cref="ITaskbar"/>.
/// The control of a system component which can be loaded into the shell.
/// </summary>
public interface ISystemControl
{
/// <summary>
/// Closes any pop-up windows associated with this control.
/// Closes the control and / or any associated user interface elements.
/// </summary>
void Close();

View file

@ -126,9 +126,6 @@
<Entry key="OperationStatus_FinalizeServiceSession">
Finalizing service session
</Entry>
<Entry key="OperationStatus_InitializeActionCenter">
Initializing action center
</Entry>
<Entry key="OperationStatus_InitializeBrowser">
Initializing browser
</Entry>
@ -153,8 +150,8 @@
<Entry key="OperationStatus_InitializeSession">
Initializing new session
</Entry>
<Entry key="OperationStatus_InitializeTaskbar">
Initializing taskbar
<Entry key="OperationStatus_InitializeShell">
Initializing user interface
</Entry>
<Entry key="OperationStatus_InitializeWindowMonitoring">
Initializing window monitoring
@ -201,14 +198,11 @@
<Entry key="OperationStatus_StopWindowMonitoring">
Stopping window monitoring
</Entry>
<Entry key="OperationStatus_TerminateActionCenter">
Terminating action center
</Entry>
<Entry key="OperationStatus_TerminateBrowser">
Terminating browser
</Entry>
<Entry key="OperationStatus_TerminateTaskbar">
Terminating taskbar
<Entry key="OperationStatus_TerminateShell">
Terminating user interface
</Entry>
<Entry key="OperationStatus_WaitExplorerStartup">
Waiting for Windows explorer to start up

View file

@ -21,23 +21,19 @@ namespace SafeExamBrowser.SystemComponents
private IList<KeyboardLayoutDefinition> layouts = new List<KeyboardLayoutDefinition>();
private ILogger logger;
private InputLanguage originalLanguage;
private ISystemKeyboardLayoutControl control;
private IList<ISystemKeyboardLayoutControl> controls;
private IText text;
public KeyboardLayout(ILogger logger, IText text)
{
this.controls = new List<ISystemKeyboardLayoutControl>();
this.logger = logger;
this.text = text;
}
public void Initialize(ISystemKeyboardLayoutControl control)
public void Initialize()
{
this.control = control;
originalLanguage = InputLanguage.CurrentInputLanguage;
control.LayoutSelected += Control_LayoutSelected;
control.SetTooltip(text.Get(TextKey.SystemControl_KeyboardLayoutTooltip));
logger.Info($"Saved current keyboard layout {ToString(originalLanguage)}.");
foreach (InputLanguage language in InputLanguage.InstalledInputLanguages)
@ -50,22 +46,36 @@ namespace SafeExamBrowser.SystemComponents
Name = language.LayoutName
};
control.Add(layout);
layouts.Add(layout);
logger.Info($"Added keyboard layout {ToString(language)} to system control.");
logger.Info($"Detected keyboard layout {ToString(language)}.");
}
}
public void Register(ISystemKeyboardLayoutControl control)
{
control.LayoutSelected += Control_LayoutSelected;
control.SetTooltip(text.Get(TextKey.SystemControl_KeyboardLayoutTooltip));
foreach (var layout in layouts)
{
control.Add(layout);
}
controls.Add(control);
}
public void Terminate()
{
control?.Close();
if (originalLanguage != null)
{
InputLanguage.CurrentInputLanguage = originalLanguage;
logger.Info($"Restored original keyboard layout {ToString(originalLanguage)}.");
}
foreach (var control in controls)
{
control.Close();
}
}
private void Control_LayoutSelected(IKeyboardLayout layout)

View file

@ -7,6 +7,7 @@
*/
using System;
using System.Collections.Generic;
using System.Timers;
using SafeExamBrowser.Contracts.I18n;
using SafeExamBrowser.Contracts.Logging;
@ -23,22 +24,19 @@ namespace SafeExamBrowser.SystemComponents
private bool infoShown, warningShown;
private ILogger logger;
private ISystemPowerSupplyControl control;
private IList<ISystemPowerSupplyControl> controls;
private IText text;
private Timer timer;
public PowerSupply(ILogger logger, IText text)
{
this.controls = new List<ISystemPowerSupplyControl>();
this.logger = logger;
this.text = text;
}
public void Initialize(ISystemPowerSupplyControl control)
public void Initialize()
{
this.control = control;
UpdateControl();
timer = new Timer(TWO_SECONDS);
timer.Elapsed += Timer_Elapsed;
timer.AutoReset = true;
@ -47,19 +45,32 @@ namespace SafeExamBrowser.SystemComponents
logger.Info("Started monitoring the power supply.");
}
public void Register(ISystemPowerSupplyControl control)
{
controls.Add(control);
UpdateControls();
}
public void Terminate()
{
timer?.Stop();
control?.Close();
logger.Info("Stopped monitoring the power supply.");
if (timer != null)
{
timer.Stop();
logger.Info("Stopped monitoring the power supply.");
}
foreach (var control in controls)
{
control.Close();
}
}
private void Timer_Elapsed(object sender, ElapsedEventArgs e)
{
UpdateControl();
UpdateControls();
}
private void UpdateControl()
private void UpdateControls()
{
var charge = SystemInformation.PowerStatus.BatteryLifePercent;
var percentage = Math.Round(charge * 100);
@ -67,32 +78,35 @@ namespace SafeExamBrowser.SystemComponents
var online = SystemInformation.PowerStatus.PowerLineStatus == PowerLineStatus.Online;
var tooltip = string.Empty;
if (online)
foreach (var control in controls)
{
tooltip = text.Get(percentage == 100 ? TextKey.SystemControl_BatteryCharged : TextKey.SystemControl_BatteryCharging);
infoShown = false;
warningShown = false;
if (online)
{
tooltip = text.Get(percentage == 100 ? TextKey.SystemControl_BatteryCharged : TextKey.SystemControl_BatteryCharging);
infoShown = false;
warningShown = false;
}
else
{
var hours = SystemInformation.PowerStatus.BatteryLifeRemaining / 3600;
var minutes = (SystemInformation.PowerStatus.BatteryLifeRemaining - (hours * 3600)) / 60;
HandleBatteryStatus(control, status);
tooltip = text.Get(TextKey.SystemControl_BatteryRemainingCharge);
tooltip = tooltip.Replace("%%HOURS%%", hours.ToString());
tooltip = tooltip.Replace("%%MINUTES%%", minutes.ToString());
}
tooltip = tooltip.Replace("%%CHARGE%%", percentage.ToString());
control.SetBatteryCharge(charge, status);
control.SetPowerGridConnection(online);
control.SetTooltip(tooltip);
}
else
{
var hours = SystemInformation.PowerStatus.BatteryLifeRemaining / 3600;
var minutes = (SystemInformation.PowerStatus.BatteryLifeRemaining - (hours * 3600)) / 60;
HandleBatteryStatus(status);
tooltip = text.Get(TextKey.SystemControl_BatteryRemainingCharge);
tooltip = tooltip.Replace("%%HOURS%%", hours.ToString());
tooltip = tooltip.Replace("%%MINUTES%%", minutes.ToString());
}
tooltip = tooltip.Replace("%%CHARGE%%", percentage.ToString());
control.SetBatteryCharge(charge, status);
control.SetPowerGridConnection(online);
control.SetTooltip(tooltip);
}
private void HandleBatteryStatus(BatteryChargeStatus status)
private void HandleBatteryStatus(ISystemPowerSupplyControl control, BatteryChargeStatus status)
{
if (status == BatteryChargeStatus.Low && !infoShown)
{

View file

@ -25,38 +25,30 @@ namespace SafeExamBrowser.SystemComponents
private const int TWO_SECONDS = 2000;
private readonly object @lock = new object();
private ISystemWirelessNetworkControl control;
private IList<ISystemWirelessNetworkControl> controls;
private IList<WirelessNetworkDefinition> networks;
private bool hasWifiAdapter;
private ILogger logger;
private IList<WirelessNetworkDefinition> networks = new List<WirelessNetworkDefinition>();
private IText text;
private Timer timer;
private Wifi wifi;
public WirelessNetwork(ILogger logger, IText text)
{
this.controls = new List<ISystemWirelessNetworkControl>();
this.logger = logger;
this.networks = new List<WirelessNetworkDefinition>();
this.text = text;
}
public void Initialize(ISystemWirelessNetworkControl control)
public void Initialize()
{
this.control = control;
this.wifi = new Wifi();
wifi = new Wifi();
wifi.ConnectionStatusChanged += Wifi_ConnectionStatusChanged;
hasWifiAdapter = !wifi.NoWifiAvailable && !IsTurnedOff();
if (wifi.NoWifiAvailable || IsTurnedOff())
if (hasWifiAdapter)
{
control.HasWirelessNetworkAdapter = false;
control.SetTooltip(text.Get(TextKey.SystemControl_WirelessNotAvailable));
logger.Info("Wireless networks cannot be monitored, as there is no hardware adapter available or it is turned off.");
}
else
{
control.HasWirelessNetworkAdapter = true;
control.NetworkSelected += Control_NetworkSelected;
wifi.ConnectionStatusChanged += Wifi_ConnectionStatusChanged;
UpdateControl();
timer = new Timer(TWO_SECONDS);
timer.Elapsed += Timer_Elapsed;
timer.AutoReset = true;
@ -64,17 +56,45 @@ namespace SafeExamBrowser.SystemComponents
logger.Info("Started monitoring the wireless network adapter.");
}
else
{
logger.Info("Wireless networks cannot be monitored, as there is no hardware adapter available or it is turned off.");
}
}
public void Register(ISystemWirelessNetworkControl control)
{
if (hasWifiAdapter)
{
control.HasWirelessNetworkAdapter = true;
control.NetworkSelected += Control_NetworkSelected;
}
else
{
control.HasWirelessNetworkAdapter = false;
control.SetTooltip(text.Get(TextKey.SystemControl_WirelessNotAvailable));
}
controls.Add(control);
if (hasWifiAdapter)
{
UpdateControls();
}
}
public void Terminate()
{
timer?.Stop();
control?.Close();
if (timer != null)
{
timer.Stop();
logger.Info("Stopped monitoring the wireless network adapter.");
}
foreach (var control in controls)
{
control.Close();
}
}
private void Control_NetworkSelected(IWirelessNetwork network)
@ -85,7 +105,11 @@ namespace SafeExamBrowser.SystemComponents
var authRequest = new AuthRequest(accessPoint);
accessPoint.ConnectAsync(authRequest, false, (success) => AccessPoint_OnConnectComplete(network.Name, success));
control.IsConnecting = true;
foreach (var control in controls)
{
control.IsConnecting = true;
}
}
catch (Exception e)
{
@ -104,18 +128,22 @@ namespace SafeExamBrowser.SystemComponents
logger.Error($"Failed to connect to wireless network '{name}!'");
}
control.IsConnecting = false;
UpdateControl();
foreach (var control in controls)
{
control.IsConnecting = false;
}
UpdateControls();
}
private void Timer_Elapsed(object sender, ElapsedEventArgs e)
{
UpdateControl();
UpdateControls();
}
private void Wifi_ConnectionStatusChanged(object sender, WifiStatusEventArgs e)
{
UpdateControl();
UpdateControls();
}
private bool IsTurnedOff()
@ -143,12 +171,15 @@ namespace SafeExamBrowser.SystemComponents
return true;
}
private void UpdateControl()
private void UpdateControls()
{
lock (@lock)
{
try
{
var isConnected = false;
var networkName = string.Empty;
networks.Clear();
foreach (var accessPoint in wifi.GetAccessPoints())
@ -156,22 +187,28 @@ namespace SafeExamBrowser.SystemComponents
// The user may only connect to an already configured wireless network!
if (accessPoint.HasProfile)
{
networks.Add(ToDefinition(accessPoint));
networkName = accessPoint.Name;
isConnected = accessPoint.IsConnected;
if (accessPoint.IsConnected)
{
control.SetTooltip(text.Get(TextKey.SystemControl_WirelessConnected).Replace("%%NAME%%", accessPoint.Name));
}
networks.Add(ToDefinition(accessPoint));
}
}
if (wifi.ConnectionStatus == WifiStatus.Disconnected)
foreach (var control in controls)
{
control.SetTooltip(text.Get(TextKey.SystemControl_WirelessDisconnected));
}
if (wifi.ConnectionStatus == WifiStatus.Disconnected)
{
control.SetTooltip(text.Get(TextKey.SystemControl_WirelessDisconnected));
}
control.NetworkStatus = ToStatus(wifi.ConnectionStatus);
control.Update(new List<IWirelessNetwork>(networks));
if (isConnected)
{
control.SetTooltip(text.Get(TextKey.SystemControl_WirelessConnected).Replace("%%NAME%%", networkName));
}
control.NetworkStatus = ToStatus(wifi.ConnectionStatus);
control.Update(new List<IWirelessNetwork>(networks));
}
}
catch (Exception e)
{

View file

@ -20,14 +20,6 @@
<ScrollViewer Grid.Row="0" HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto" Template="{StaticResource SmallBarScrollViewer}">
<StackPanel x:Name="ApplicationPanel" Orientation="Vertical" />
</ScrollViewer>
<UniformGrid x:Name="ControlPanel" Grid.Row="1" Columns="4" Margin="5">
<Label Height="60">Control</Label>
<Label Height="60">Control</Label>
<Label Height="60">Control</Label>
<Label Height="60">Control</Label>
<Label Height="60">Control</Label>
<Label Height="60">Control</Label>
<Label Height="60">Control</Label>
</UniformGrid>
<UniformGrid x:Name="ControlPanel" Grid.Row="1" Columns="4" Margin="5" />
</Grid>
</Window>

View file

@ -33,12 +33,18 @@ namespace SafeExamBrowser.UserInterface.Desktop
public void AddNotificationControl(INotificationControl control)
{
if (control is UIElement uiElement)
{
ControlPanel.Children.Add(uiElement);
}
}
public void AddSystemControl(ISystemControl control)
{
if (control is UIElement uiElement)
{
ControlPanel.Children.Add(uiElement);
}
}
public new void Close()

View file

@ -0,0 +1,28 @@
<UserControl x:Class="SafeExamBrowser.UserInterface.Desktop.Controls.ActionCenterNotificationButton"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:SafeExamBrowser.UserInterface.Desktop.Controls"
mc:Ignorable="d" d:DesignHeight="60" d:DesignWidth="80">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="../Templates/Buttons.xaml" />
<ResourceDictionary Source="../Templates/Colors.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid Background="#AA808080" Margin="5">
<Button x:Name="IconButton" Click="Icon_Click" Padding="2" Template="{StaticResource ActionCenterButton}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="2*" />
<RowDefinition Height="1*" />
</Grid.RowDefinitions>
<ContentControl Grid.Row="0" x:Name="Icon" Margin="0,5,0,10" MaxHeight="25" />
<TextBlock Grid.Row="1" x:Name="Text" FontSize="11" Foreground="White" TextAlignment="Left" TextTrimming="CharacterEllipsis" TextWrapping="NoWrap" />
</Grid>
</Button>
</Grid>
</UserControl>

View file

@ -0,0 +1,40 @@
/*
* Copyright (c) 2019 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.Windows;
using System.Windows.Controls;
using SafeExamBrowser.Contracts.Client;
using SafeExamBrowser.Contracts.UserInterface.Shell;
using SafeExamBrowser.Contracts.UserInterface.Shell.Events;
using SafeExamBrowser.UserInterface.Desktop.Utilities;
namespace SafeExamBrowser.UserInterface.Desktop.Controls
{
public partial class ActionCenterNotificationButton : UserControl, INotificationControl
{
public event NotificationControlClickedEventHandler Clicked;
public ActionCenterNotificationButton(INotificationInfo info)
{
InitializeComponent();
InitializeNotificationIcon(info);
}
private void Icon_Click(object sender, RoutedEventArgs e)
{
Clicked?.Invoke();
}
private void InitializeNotificationIcon(INotificationInfo info)
{
Icon.Content = IconResourceLoader.Load(info.IconResource);
IconButton.ToolTip = info.Tooltip;
Text.Text = info.Tooltip;
}
}
}

View file

@ -1,4 +1,4 @@
<UserControl x:Class="SafeExamBrowser.UserInterface.Desktop.Controls.NotificationButton"
<UserControl x:Class="SafeExamBrowser.UserInterface.Desktop.Controls.TaskbarNotificationButton"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

View file

@ -15,11 +15,11 @@ using SafeExamBrowser.UserInterface.Desktop.Utilities;
namespace SafeExamBrowser.UserInterface.Desktop.Controls
{
public partial class NotificationButton : UserControl, INotificationControl
public partial class TaskbarNotificationButton : UserControl, INotificationControl
{
public event NotificationControlClickedEventHandler Clicked;
public NotificationButton(INotificationInfo info)
public TaskbarNotificationButton(INotificationInfo info)
{
InitializeComponent();
InitializeNotificationIcon(info);

View file

@ -80,6 +80,9 @@
<Compile Include="Controls\ActionCenterApplicationButton.xaml.cs">
<DependentUpon>ActionCenterApplicationButton.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\ActionCenterNotificationButton.xaml.cs">
<DependentUpon>ActionCenterNotificationButton.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\TaskbarApplicationControl.xaml.cs">
<DependentUpon>TaskbarApplicationControl.xaml</DependentUpon>
</Compile>
@ -95,8 +98,8 @@
<Compile Include="Controls\KeyboardLayoutControl.xaml.cs">
<DependentUpon>KeyboardLayoutControl.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\NotificationButton.xaml.cs">
<DependentUpon>NotificationButton.xaml</DependentUpon>
<Compile Include="Controls\TaskbarNotificationButton.xaml.cs">
<DependentUpon>TaskbarNotificationButton.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\PowerSupplyControl.xaml.cs">
<DependentUpon>PowerSupplyControl.xaml</DependentUpon>
@ -151,6 +154,10 @@
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Controls\ActionCenterNotificationButton.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Controls\TaskbarApplicationControl.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
@ -171,7 +178,7 @@
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Controls\NotificationButton.xaml">
<Page Include="Controls\TaskbarNotificationButton.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>

View file

@ -86,9 +86,16 @@ namespace SafeExamBrowser.UserInterface.Desktop
return logWindow;
}
public INotificationControl CreateNotificationControl(INotificationInfo info)
public INotificationControl CreateNotificationControl(INotificationInfo info, Location location)
{
return new NotificationButton(info);
if (location == Location.ActionCenter)
{
return new ActionCenterNotificationButton(info);
}
else
{
return new TaskbarNotificationButton(info);
}
}
public IPasswordDialog CreatePasswordDialog(string message, string title)