SEBWIN-308: Implemented basic download overview for browser.
This commit is contained in:
		
							parent
							
								
									b9536c6a1b
								
							
						
					
					
						commit
						97f3fb4a02
					
				
					 30 changed files with 582 additions and 133 deletions
				
			
		| 
						 | 
				
			
			@ -23,6 +23,7 @@ using SafeExamBrowser.Logging.Contracts;
 | 
			
		|||
using SafeExamBrowser.Settings.Browser.Proxy;
 | 
			
		||||
using SafeExamBrowser.Settings.Logging;
 | 
			
		||||
using SafeExamBrowser.UserInterface.Contracts;
 | 
			
		||||
using SafeExamBrowser.UserInterface.Contracts.FileSystemDialog;
 | 
			
		||||
using SafeExamBrowser.UserInterface.Contracts.MessageBox;
 | 
			
		||||
using BrowserSettings = SafeExamBrowser.Settings.Browser.BrowserSettings;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -34,6 +35,7 @@ namespace SafeExamBrowser.Browser
 | 
			
		|||
 | 
			
		||||
		private AppConfig appConfig;
 | 
			
		||||
		private List<BrowserApplicationInstance> instances;
 | 
			
		||||
		private IFileSystemDialog fileSystemDialog;
 | 
			
		||||
		private IMessageBox messageBox;
 | 
			
		||||
		private IModuleLogger logger;
 | 
			
		||||
		private BrowserSettings settings;
 | 
			
		||||
| 
						 | 
				
			
			@ -53,12 +55,14 @@ namespace SafeExamBrowser.Browser
 | 
			
		|||
		public BrowserApplication(
 | 
			
		||||
			AppConfig appConfig,
 | 
			
		||||
			BrowserSettings settings,
 | 
			
		||||
			IFileSystemDialog fileSystemDialog,
 | 
			
		||||
			IMessageBox messageBox,
 | 
			
		||||
			IModuleLogger logger,
 | 
			
		||||
			IText text,
 | 
			
		||||
			IUserInterfaceFactory uiFactory)
 | 
			
		||||
		{
 | 
			
		||||
			this.appConfig = appConfig;
 | 
			
		||||
			this.fileSystemDialog = fileSystemDialog;
 | 
			
		||||
			this.instances = new List<BrowserApplicationInstance>();
 | 
			
		||||
			this.logger = logger;
 | 
			
		||||
			this.messageBox = messageBox;
 | 
			
		||||
| 
						 | 
				
			
			@ -122,7 +126,7 @@ namespace SafeExamBrowser.Browser
 | 
			
		|||
			var isMainInstance = instances.Count == 0;
 | 
			
		||||
			var instanceLogger = logger.CloneFor($"Browser Instance #{id}");
 | 
			
		||||
			var startUrl = url ?? settings.StartUrl;
 | 
			
		||||
			var instance = new BrowserApplicationInstance(appConfig, settings, id, isMainInstance, messageBox, instanceLogger, text, uiFactory, startUrl);
 | 
			
		||||
			var instance = new BrowserApplicationInstance(appConfig, settings, id, isMainInstance, fileSystemDialog, messageBox, instanceLogger, text, uiFactory, startUrl);
 | 
			
		||||
 | 
			
		||||
			instance.ConfigurationDownloadRequested += (fileName, args) => ConfigurationDownloadRequested?.Invoke(fileName, args);
 | 
			
		||||
			instance.PopupRequested += Instance_PopupRequested;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -24,6 +24,8 @@ using SafeExamBrowser.Settings.Browser;
 | 
			
		|||
using SafeExamBrowser.Settings.Browser.Filter;
 | 
			
		||||
using SafeExamBrowser.UserInterface.Contracts;
 | 
			
		||||
using SafeExamBrowser.UserInterface.Contracts.Browser;
 | 
			
		||||
using SafeExamBrowser.UserInterface.Contracts.Browser.Data;
 | 
			
		||||
using SafeExamBrowser.UserInterface.Contracts.FileSystemDialog;
 | 
			
		||||
using SafeExamBrowser.UserInterface.Contracts.MessageBox;
 | 
			
		||||
 | 
			
		||||
namespace SafeExamBrowser.Browser
 | 
			
		||||
| 
						 | 
				
			
			@ -37,6 +39,7 @@ namespace SafeExamBrowser.Browser
 | 
			
		|||
		private IBrowserWindow window;
 | 
			
		||||
		private HttpClient httpClient;
 | 
			
		||||
		private bool isMainInstance;
 | 
			
		||||
		private IFileSystemDialog fileSystemDialog;
 | 
			
		||||
		private IMessageBox messageBox;
 | 
			
		||||
		private IModuleLogger logger;
 | 
			
		||||
		private BrowserSettings settings;
 | 
			
		||||
| 
						 | 
				
			
			@ -69,6 +72,7 @@ namespace SafeExamBrowser.Browser
 | 
			
		|||
			BrowserSettings settings,
 | 
			
		||||
			int id,
 | 
			
		||||
			bool isMainInstance,
 | 
			
		||||
			IFileSystemDialog fileSystemDialog,
 | 
			
		||||
			IMessageBox messageBox,
 | 
			
		||||
			IModuleLogger logger,
 | 
			
		||||
			IText text,
 | 
			
		||||
| 
						 | 
				
			
			@ -79,6 +83,7 @@ namespace SafeExamBrowser.Browser
 | 
			
		|||
			this.Id = id;
 | 
			
		||||
			this.httpClient = new HttpClient();
 | 
			
		||||
			this.isMainInstance = isMainInstance;
 | 
			
		||||
			this.fileSystemDialog = fileSystemDialog;
 | 
			
		||||
			this.messageBox = messageBox;
 | 
			
		||||
			this.logger = logger;
 | 
			
		||||
			this.settings = settings;
 | 
			
		||||
| 
						 | 
				
			
			@ -122,6 +127,7 @@ namespace SafeExamBrowser.Browser
 | 
			
		|||
			displayHandler.FaviconChanged += DisplayHandler_FaviconChanged;
 | 
			
		||||
			displayHandler.ProgressChanged += DisplayHandler_ProgressChanged;
 | 
			
		||||
			downloadHandler.ConfigurationDownloadRequested += DownloadHandler_ConfigurationDownloadRequested;
 | 
			
		||||
			downloadHandler.DownloadUpdated += DownloadHandler_DownloadUpdated;
 | 
			
		||||
			keyboardHandler.ReloadRequested += ReloadRequested;
 | 
			
		||||
			keyboardHandler.ZoomInRequested += ZoomInRequested;
 | 
			
		||||
			keyboardHandler.ZoomOutRequested += ZoomOutRequested;
 | 
			
		||||
| 
						 | 
				
			
			@ -211,8 +217,7 @@ namespace SafeExamBrowser.Browser
 | 
			
		|||
 | 
			
		||||
		private void DialogHandler_DialogRequested(DialogRequestedEventArgs args)
 | 
			
		||||
		{
 | 
			
		||||
			var dialog = uiFactory.CreateFileSystemDialog(args.Element, args.InitialPath, args.Operation, title: args.Title);
 | 
			
		||||
			var result = dialog.Show(window);
 | 
			
		||||
			var result = fileSystemDialog.Show(args.Element, args.InitialPath, args.Operation, title: args.Title, owner: window);
 | 
			
		||||
 | 
			
		||||
			if (result.Success)
 | 
			
		||||
			{
 | 
			
		||||
| 
						 | 
				
			
			@ -269,6 +274,11 @@ namespace SafeExamBrowser.Browser
 | 
			
		|||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		private void DownloadHandler_DownloadUpdated(DownloadItemState state)
 | 
			
		||||
		{
 | 
			
		||||
			window.UpdateDownloadState(state);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		private void LifeSpanHandler_PopupRequested(PopupRequestedEventArgs args)
 | 
			
		||||
		{
 | 
			
		||||
			var validCurrentUri = Uri.TryCreate(control.Address, UriKind.Absolute, out var currentUri);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,14 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright (c) 2020 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.UserInterface.Contracts.Browser.Data;
 | 
			
		||||
 | 
			
		||||
namespace SafeExamBrowser.Browser.Events
 | 
			
		||||
{
 | 
			
		||||
	internal delegate void DownloadUpdatedEventHandler(DownloadItemState state);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -12,8 +12,10 @@ using System.IO;
 | 
			
		|||
using System.Threading.Tasks;
 | 
			
		||||
using CefSharp;
 | 
			
		||||
using SafeExamBrowser.Browser.Contracts.Events;
 | 
			
		||||
using SafeExamBrowser.Browser.Events;
 | 
			
		||||
using SafeExamBrowser.Configuration.Contracts;
 | 
			
		||||
using SafeExamBrowser.Logging.Contracts;
 | 
			
		||||
using SafeExamBrowser.UserInterface.Contracts.Browser.Data;
 | 
			
		||||
using Syroot.Windows.IO;
 | 
			
		||||
using BrowserSettings = SafeExamBrowser.Settings.Browser.BrowserSettings;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -24,14 +26,17 @@ namespace SafeExamBrowser.Browser.Handlers
 | 
			
		|||
		private AppConfig appConfig;
 | 
			
		||||
		private BrowserSettings settings;
 | 
			
		||||
		private ConcurrentDictionary<int, DownloadFinishedCallback> callbacks;
 | 
			
		||||
		private ConcurrentDictionary<int, Guid> downloads;
 | 
			
		||||
		private ILogger logger;
 | 
			
		||||
 | 
			
		||||
		internal event DownloadRequestedEventHandler ConfigurationDownloadRequested;
 | 
			
		||||
		internal event DownloadUpdatedEventHandler DownloadUpdated;
 | 
			
		||||
 | 
			
		||||
		internal DownloadHandler(AppConfig appConfig, BrowserSettings settings, ILogger logger)
 | 
			
		||||
		{
 | 
			
		||||
			this.appConfig = appConfig;
 | 
			
		||||
			this.callbacks = new ConcurrentDictionary<int, DownloadFinishedCallback>();
 | 
			
		||||
			this.downloads = new ConcurrentDictionary<int, Guid>();
 | 
			
		||||
			this.logger = logger;
 | 
			
		||||
			this.settings = settings;
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -60,18 +65,35 @@ namespace SafeExamBrowser.Browser.Handlers
 | 
			
		|||
 | 
			
		||||
		public void OnDownloadUpdated(IWebBrowser webBrowser, IBrowser browser, DownloadItem downloadItem, IDownloadItemCallback callback)
 | 
			
		||||
		{
 | 
			
		||||
			// TODO: Show download progress in respective window -> event for BrowserApplicationInstance!
 | 
			
		||||
			var hasId = downloads.TryGetValue(downloadItem.Id, out var id);
 | 
			
		||||
 | 
			
		||||
			if (hasId)
 | 
			
		||||
			{
 | 
			
		||||
				var state = new DownloadItemState(id)
 | 
			
		||||
				{
 | 
			
		||||
					Completion = downloadItem.PercentComplete / 100.0,
 | 
			
		||||
					FullPath = downloadItem.FullPath,
 | 
			
		||||
					IsCancelled = downloadItem.IsCancelled,
 | 
			
		||||
					IsComplete = downloadItem.IsComplete,
 | 
			
		||||
					Url = downloadItem.Url
 | 
			
		||||
				};
 | 
			
		||||
				
 | 
			
		||||
				Task.Run(() => DownloadUpdated?.Invoke(state));
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if (downloadItem.IsComplete || downloadItem.IsCancelled)
 | 
			
		||||
			{
 | 
			
		||||
				logger.Debug($"Download of '{downloadItem.Url}' {(downloadItem.IsComplete ? "is complete" : "was cancelled")}.");
 | 
			
		||||
 | 
			
		||||
				if (callbacks.TryRemove(downloadItem.Id, out DownloadFinishedCallback finished) && finished != null)
 | 
			
		||||
				{
 | 
			
		||||
					Task.Run(() => finished.Invoke(downloadItem.IsComplete, downloadItem.FullPath));
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				logger.Debug($"Download of '{downloadItem.Url}' {(downloadItem.IsComplete ? "is complete" : "was cancelled")}.");
 | 
			
		||||
 | 
			
		||||
				// TODO: Show success message or download icon like Firefox in respective window!
 | 
			
		||||
				if (hasId)
 | 
			
		||||
				{
 | 
			
		||||
					downloads.TryRemove(downloadItem.Id, out _);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -100,6 +122,8 @@ namespace SafeExamBrowser.Browser.Handlers
 | 
			
		|||
				logger.Debug($"Automatically downloading file as '{filePath}'.");
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			downloads[downloadItem.Id] = Guid.NewGuid();
 | 
			
		||||
 | 
			
		||||
			using (callback)
 | 
			
		||||
			{
 | 
			
		||||
				callback.Continue(filePath, showDialog);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -68,6 +68,7 @@
 | 
			
		|||
    <Compile Include="BrowserApplicationInstance.cs" />
 | 
			
		||||
    <Compile Include="Events\DialogRequestedEventArgs.cs" />
 | 
			
		||||
    <Compile Include="Events\DialogRequestedEventHandler.cs" />
 | 
			
		||||
    <Compile Include="Events\DownloadUpdatedEventHandler.cs" />
 | 
			
		||||
    <Compile Include="Events\FaviconChangedEventHandler.cs" />
 | 
			
		||||
    <Compile Include="Events\InstanceTerminatedEventHandler.cs" />
 | 
			
		||||
    <Compile Include="Events\PopupRequestedEventArgs.cs" />
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -42,6 +42,7 @@ using SafeExamBrowser.SystemComponents.Keyboard;
 | 
			
		|||
using SafeExamBrowser.SystemComponents.PowerSupply;
 | 
			
		||||
using SafeExamBrowser.SystemComponents.WirelessNetwork;
 | 
			
		||||
using SafeExamBrowser.UserInterface.Contracts;
 | 
			
		||||
using SafeExamBrowser.UserInterface.Contracts.FileSystemDialog;
 | 
			
		||||
using SafeExamBrowser.UserInterface.Contracts.MessageBox;
 | 
			
		||||
using SafeExamBrowser.UserInterface.Contracts.Shell;
 | 
			
		||||
using SafeExamBrowser.UserInterface.Shared.Activators;
 | 
			
		||||
| 
						 | 
				
			
			@ -197,8 +198,9 @@ namespace SafeExamBrowser.Client
 | 
			
		|||
 | 
			
		||||
		private IOperation BuildBrowserOperation()
 | 
			
		||||
		{
 | 
			
		||||
			var fileSystemDialog = BuildFileSystemDialog();
 | 
			
		||||
			var moduleLogger = ModuleLogger(nameof(BrowserApplication));
 | 
			
		||||
			var browser = new BrowserApplication(context.AppConfig, context.Settings.Browser, messageBox, moduleLogger, text, uiFactory);
 | 
			
		||||
			var browser = new BrowserApplication(context.AppConfig, context.Settings.Browser, fileSystemDialog, messageBox, moduleLogger, text, uiFactory);
 | 
			
		||||
			var operation = new BrowserOperation(actionCenter, context, logger, taskbar, taskview, uiFactory);
 | 
			
		||||
 | 
			
		||||
			context.Browser = browser;
 | 
			
		||||
| 
						 | 
				
			
			@ -282,6 +284,17 @@ namespace SafeExamBrowser.Client
 | 
			
		|||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		private IFileSystemDialog BuildFileSystemDialog()
 | 
			
		||||
		{
 | 
			
		||||
			switch (uiMode)
 | 
			
		||||
			{
 | 
			
		||||
				case UserInterfaceMode.Mobile:
 | 
			
		||||
					return new Mobile.FileSystemDialogFactory(text);
 | 
			
		||||
				default:
 | 
			
		||||
					return new Desktop.FileSystemDialogFactory(text);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		private IMessageBox BuildMessageBox()
 | 
			
		||||
		{
 | 
			
		||||
			switch (uiMode)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -22,6 +22,9 @@ namespace SafeExamBrowser.I18n.Contracts
 | 
			
		|||
		Browser_Name,
 | 
			
		||||
		Browser_Tooltip,
 | 
			
		||||
		BrowserWindow_DeveloperConsoleMenuItem,
 | 
			
		||||
		BrowserWindow_Downloading,
 | 
			
		||||
		BrowserWindow_DownloadCancelled,
 | 
			
		||||
		BrowserWindow_DownloadComplete,
 | 
			
		||||
		BrowserWindow_ZoomMenuItem,
 | 
			
		||||
		Build,
 | 
			
		||||
		FileSystemDialog_Cancel,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -24,6 +24,15 @@
 | 
			
		|||
    <Entry key="BrowserWindow_DeveloperConsoleMenuItem">
 | 
			
		||||
        Developer Console
 | 
			
		||||
    </Entry>
 | 
			
		||||
    <Entry key="BrowserWindow_Downloading">
 | 
			
		||||
        Downloading...
 | 
			
		||||
    </Entry>
 | 
			
		||||
    <Entry key="BrowserWindow_DownloadCancelled">
 | 
			
		||||
        Cancelled.
 | 
			
		||||
    </Entry>
 | 
			
		||||
    <Entry key="BrowserWindow_DownloadComplete">
 | 
			
		||||
        Downloaded.
 | 
			
		||||
    </Entry>
 | 
			
		||||
    <Entry key="BrowserWindow_ZoomMenuItem">
 | 
			
		||||
        Page Zoom
 | 
			
		||||
    </Entry>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,53 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright (c) 2020 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;
 | 
			
		||||
 | 
			
		||||
namespace SafeExamBrowser.UserInterface.Contracts.Browser.Data
 | 
			
		||||
{
 | 
			
		||||
	/// <summary>
 | 
			
		||||
	/// Defines the state of a download item.
 | 
			
		||||
	/// </summary>
 | 
			
		||||
	public class DownloadItemState
 | 
			
		||||
	{
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// The current completion of the item, as percentage value from <c>0.0</c> to <c>1.0</c>.
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		public double Completion { get; set; }
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// The full path of the download location for the item.
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		public string FullPath { get; set; }
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// The unique identifier of the item.
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		public Guid Id { get; set; }
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// Indicates that the download was cancelled.
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		public bool IsCancelled { get; set; }
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// Indicates that the download was completed.
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		public bool IsComplete { get; set; }
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// The download URL of the item.
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		public string Url { get; set; }
 | 
			
		||||
 | 
			
		||||
		public DownloadItemState(Guid id)
 | 
			
		||||
		{
 | 
			
		||||
			Id = id;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -8,6 +8,7 @@
 | 
			
		|||
 | 
			
		||||
using System;
 | 
			
		||||
using SafeExamBrowser.Applications.Contracts.Resources.Icons;
 | 
			
		||||
using SafeExamBrowser.UserInterface.Contracts.Browser.Data;
 | 
			
		||||
using SafeExamBrowser.UserInterface.Contracts.Browser.Events;
 | 
			
		||||
using SafeExamBrowser.UserInterface.Contracts.Windows;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -83,6 +84,11 @@ namespace SafeExamBrowser.UserInterface.Contracts.Browser
 | 
			
		|||
		/// </summary>
 | 
			
		||||
		void UpdateIcon(IconResource icon);
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// Updates the download state for the given item.
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		void UpdateDownloadState(DownloadItemState state);
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// Updates the loading state according to the given value.
 | 
			
		||||
		/// </summary>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -13,8 +13,8 @@ namespace SafeExamBrowser.UserInterface.Contracts.FileSystemDialog
 | 
			
		|||
	public interface IFileSystemDialog
 | 
			
		||||
	{
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// Shows the dialog to the user. If a parent window is specified, the dialog is rendered modally for the given parent.
 | 
			
		||||
		/// Creates a dialog according to the given parameters and shows it to the user.
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		FileSystemDialogResult Show(IWindow parent = null);
 | 
			
		||||
		FileSystemDialogResult Show(FileSystemElement element, string initialPath, FileSystemOperation operation, string message = default(string), string title = default(string), IWindow owner = default(IWindow));
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -18,7 +18,6 @@ using SafeExamBrowser.SystemComponents.Contracts.Keyboard;
 | 
			
		|||
using SafeExamBrowser.SystemComponents.Contracts.PowerSupply;
 | 
			
		||||
using SafeExamBrowser.SystemComponents.Contracts.WirelessNetwork;
 | 
			
		||||
using SafeExamBrowser.UserInterface.Contracts.Browser;
 | 
			
		||||
using SafeExamBrowser.UserInterface.Contracts.FileSystemDialog;
 | 
			
		||||
using SafeExamBrowser.UserInterface.Contracts.Shell;
 | 
			
		||||
using SafeExamBrowser.UserInterface.Contracts.Windows;
 | 
			
		||||
using SafeExamBrowser.UserInterface.Contracts.Windows.Data;
 | 
			
		||||
| 
						 | 
				
			
			@ -50,11 +49,6 @@ namespace SafeExamBrowser.UserInterface.Contracts
 | 
			
		|||
		/// </summary>
 | 
			
		||||
		IBrowserWindow CreateBrowserWindow(IBrowserControl control, BrowserSettings settings, bool isMainWindow);
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// Creates a file system dialog according to the given parameters.
 | 
			
		||||
		/// </summary>
 | 
			
		||||
		IFileSystemDialog CreateFileSystemDialog(FileSystemElement element, string initialPath, FileSystemOperation operation, string message = default(string), string title = default(string));
 | 
			
		||||
 | 
			
		||||
		/// <summary>
 | 
			
		||||
		/// Creates a folder dialog with the given message.
 | 
			
		||||
		/// </summary>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -54,6 +54,7 @@
 | 
			
		|||
    <Reference Include="Microsoft.CSharp" />
 | 
			
		||||
  </ItemGroup>
 | 
			
		||||
  <ItemGroup>
 | 
			
		||||
    <Compile Include="Browser\Data\DownloadItemState.cs" />
 | 
			
		||||
    <Compile Include="Browser\Events\AddressChangedEventHandler.cs" />
 | 
			
		||||
    <Compile Include="Browser\Events\LoadingStateChangedEventHandler.cs" />
 | 
			
		||||
    <Compile Include="Browser\Events\TitleChangedEventHandler.cs" />
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -33,12 +33,21 @@
 | 
			
		|||
                        <ColumnDefinition Width="Auto" />
 | 
			
		||||
                        <ColumnDefinition Width="*" />
 | 
			
		||||
                        <ColumnDefinition Width="Auto" />
 | 
			
		||||
                        <ColumnDefinition Width="Auto" />
 | 
			
		||||
                    </Grid.ColumnDefinitions>
 | 
			
		||||
                    <Button Grid.Column="0" x:Name="BackwardButton" Height="30" HorizontalAlignment="Center" Margin="5" Template="{StaticResource BrowserButton}" VerticalAlignment="Center" />
 | 
			
		||||
                    <Button Grid.Column="1" x:Name="ForwardButton" Height="30" HorizontalAlignment="Center" Margin="5" Template="{StaticResource BrowserButton}" VerticalAlignment="Center" />
 | 
			
		||||
                    <Button Grid.Column="2" x:Name="ReloadButton" Height="30" HorizontalAlignment="Center" Margin="5" Template="{StaticResource BrowserButton}" VerticalAlignment="Center" />
 | 
			
		||||
                    <TextBox Grid.Column="3" x:Name="UrlTextBox" Height="25" HorizontalAlignment="Stretch" Margin="5,0" Padding="5,0" VerticalContentAlignment="Center" />
 | 
			
		||||
                    <Button Grid.Column="4" x:Name="MenuButton" Height="30" HorizontalAlignment="Center" Margin="5" Template="{StaticResource BrowserButton}" VerticalAlignment="Center" />
 | 
			
		||||
                    <Button Grid.Column="4" x:Name="DownloadsButton" Height="30" HorizontalAlignment="Center" Margin="5" Padding="5" Template="{StaticResource BrowserButton}" VerticalAlignment="Center" Visibility="Collapsed">
 | 
			
		||||
                        <fa:ImageAwesome Icon="Download" />
 | 
			
		||||
                    </Button>
 | 
			
		||||
                    <Popup x:Name="DownloadsPopup" AllowsTransparency="True" PopupAnimation="Slide" Placement="Custom" PlacementTarget="{Binding ElementName=BrowserControlHost}">
 | 
			
		||||
                        <Border Background="{StaticResource BackgroundBrush}" BorderBrush="LightGray" BorderThickness="1,0,1,1" MinWidth="250">
 | 
			
		||||
                            <StackPanel x:Name="Downloads" Orientation="Vertical" />
 | 
			
		||||
                        </Border>
 | 
			
		||||
                    </Popup>
 | 
			
		||||
                    <Button Grid.Column="5" x:Name="MenuButton" Height="30" HorizontalAlignment="Center" Margin="5" Template="{StaticResource BrowserButton}" VerticalAlignment="Center" />
 | 
			
		||||
                    <Popup x:Name="MenuPopup" IsOpen="False" AllowsTransparency="True" PopupAnimation="Slide" Placement="Custom" PlacementTarget="{Binding ElementName=BrowserControlHost}">
 | 
			
		||||
                        <Border Background="{StaticResource BackgroundBrush}" BorderBrush="LightGray" BorderThickness="1,0,1,1" Width="250">
 | 
			
		||||
                            <StackPanel Orientation="Vertical">
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -13,16 +13,17 @@ using System.Windows;
 | 
			
		|||
using System.Windows.Controls.Primitives;
 | 
			
		||||
using System.Windows.Input;
 | 
			
		||||
using System.Windows.Interop;
 | 
			
		||||
using System.Windows.Media;
 | 
			
		||||
using System.Windows.Media.Imaging;
 | 
			
		||||
using SafeExamBrowser.Applications.Contracts.Resources.Icons;
 | 
			
		||||
using SafeExamBrowser.I18n.Contracts;
 | 
			
		||||
using SafeExamBrowser.Settings.Browser;
 | 
			
		||||
using SafeExamBrowser.UserInterface.Contracts;
 | 
			
		||||
using SafeExamBrowser.UserInterface.Contracts.Browser;
 | 
			
		||||
using SafeExamBrowser.UserInterface.Contracts.Browser.Data;
 | 
			
		||||
using SafeExamBrowser.UserInterface.Contracts.Browser.Events;
 | 
			
		||||
using SafeExamBrowser.UserInterface.Contracts.Windows;
 | 
			
		||||
using SafeExamBrowser.UserInterface.Contracts.Windows.Events;
 | 
			
		||||
using SafeExamBrowser.UserInterface.Desktop.Controls.Browser;
 | 
			
		||||
using SafeExamBrowser.UserInterface.Shared.Utilities;
 | 
			
		||||
 | 
			
		||||
namespace SafeExamBrowser.UserInterface.Desktop
 | 
			
		||||
| 
						 | 
				
			
			@ -117,6 +118,36 @@ namespace SafeExamBrowser.UserInterface.Desktop
 | 
			
		|||
			});
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public void UpdateDownloadState(DownloadItemState state)
 | 
			
		||||
		{
 | 
			
		||||
			Dispatcher.InvokeAsync(() =>
 | 
			
		||||
			{
 | 
			
		||||
				var isNewItem = true;
 | 
			
		||||
 | 
			
		||||
				foreach (var child in Downloads.Children)
 | 
			
		||||
				{
 | 
			
		||||
					if (child is DownloadItemControl control && control.Id == state.Id)
 | 
			
		||||
					{
 | 
			
		||||
						control.Update(state);
 | 
			
		||||
						isNewItem = false;
 | 
			
		||||
 | 
			
		||||
						break;
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				if (isNewItem)
 | 
			
		||||
				{
 | 
			
		||||
					var control = new DownloadItemControl(state.Id, text);
 | 
			
		||||
 | 
			
		||||
					control.Update(state);
 | 
			
		||||
					Downloads.Children.Add(control);
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				DownloadsButton.Visibility = Visibility.Visible;
 | 
			
		||||
				DownloadsPopup.IsOpen = IsActive;
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public void UpdateLoadingState(bool isLoading)
 | 
			
		||||
		{
 | 
			
		||||
			Dispatcher.Invoke(() => ProgressBar.Visibility = isLoading ? Visibility.Visible : Visibility.Hidden);
 | 
			
		||||
| 
						 | 
				
			
			@ -167,7 +198,7 @@ namespace SafeExamBrowser.UserInterface.Desktop
 | 
			
		|||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		private CustomPopupPlacement[] MenuPopup_PlacementCallback(Size popupSize, Size targetSize, Point offset)
 | 
			
		||||
		private CustomPopupPlacement[] Popup_PlacementCallback(Size popupSize, Size targetSize, Point offset)
 | 
			
		||||
		{
 | 
			
		||||
			return new[]
 | 
			
		||||
			{
 | 
			
		||||
| 
						 | 
				
			
			@ -216,21 +247,23 @@ namespace SafeExamBrowser.UserInterface.Desktop
 | 
			
		|||
 | 
			
		||||
		private void RegisterEvents()
 | 
			
		||||
		{
 | 
			
		||||
			var originalBrush = MenuButton.Background;
 | 
			
		||||
 | 
			
		||||
			BackwardButton.Click += (o, args) => BackwardNavigationRequested?.Invoke();
 | 
			
		||||
			Closing += BrowserWindow_Closing;
 | 
			
		||||
			DeveloperConsoleButton.Click += (o, args) => DeveloperConsoleRequested?.Invoke();
 | 
			
		||||
			DownloadsButton.Click += (o, args) => DownloadsPopup.IsOpen = !DownloadsPopup.IsOpen;
 | 
			
		||||
			DownloadsButton.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => DownloadsPopup.IsOpen = DownloadsPopup.IsMouseOver));
 | 
			
		||||
			DownloadsPopup.CustomPopupPlacementCallback = new CustomPopupPlacementCallback(Popup_PlacementCallback);
 | 
			
		||||
			DownloadsPopup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => DownloadsPopup.IsOpen = DownloadsPopup.IsMouseOver));
 | 
			
		||||
			ForwardButton.Click += (o, args) => ForwardNavigationRequested?.Invoke();
 | 
			
		||||
			Loaded += BrowserWindow_Loaded;
 | 
			
		||||
			MenuButton.Click += (o, args) => MenuPopup.IsOpen = !MenuPopup.IsOpen;
 | 
			
		||||
			MenuButton.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => MenuPopup.IsOpen = MenuPopup.IsMouseOver));
 | 
			
		||||
			MenuPopup.CustomPopupPlacementCallback = new CustomPopupPlacementCallback(MenuPopup_PlacementCallback);
 | 
			
		||||
			MenuPopup.Closed += (o, args) => MenuButton.Background = originalBrush;
 | 
			
		||||
			MenuPopup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => MenuPopup.IsOpen = IsMouseOver));
 | 
			
		||||
			MenuPopup.Opened += (o, args) => MenuButton.Background = Brushes.LightGray;
 | 
			
		||||
			MenuPopup.CustomPopupPlacementCallback = new CustomPopupPlacementCallback(Popup_PlacementCallback);
 | 
			
		||||
			MenuPopup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => MenuPopup.IsOpen = MenuPopup.IsMouseOver));
 | 
			
		||||
			KeyUp += BrowserWindow_KeyUp;
 | 
			
		||||
			LocationChanged += (o, args) => { DownloadsPopup.IsOpen = false; MenuPopup.IsOpen = false; };
 | 
			
		||||
			ReloadButton.Click += (o, args) => ReloadRequested?.Invoke();
 | 
			
		||||
			SizeChanged += (o, args) => { DownloadsPopup.IsOpen = false; MenuPopup.IsOpen = false; };
 | 
			
		||||
			SystemParameters.StaticPropertyChanged += SystemParameters_StaticPropertyChanged;
 | 
			
		||||
			UrlTextBox.GotKeyboardFocus += (o, args) => UrlTextBox.SelectAll();
 | 
			
		||||
			UrlTextBox.GotMouseCapture += UrlTextBox_GotMouseCapture;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,22 @@
 | 
			
		|||
<UserControl x:Class="SafeExamBrowser.UserInterface.Desktop.Controls.Browser.DownloadItemControl"
 | 
			
		||||
             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.Browser"
 | 
			
		||||
             mc:Ignorable="d" d:DesignHeight="50" d:DesignWidth="200">
 | 
			
		||||
    <Grid>
 | 
			
		||||
        <Grid.ColumnDefinitions>
 | 
			
		||||
            <ColumnDefinition Width="Auto" />
 | 
			
		||||
            <ColumnDefinition Width="*" />
 | 
			
		||||
        </Grid.ColumnDefinitions>
 | 
			
		||||
        <Grid.RowDefinitions>
 | 
			
		||||
            <RowDefinition Height="*" />
 | 
			
		||||
            <RowDefinition Height="*" />
 | 
			
		||||
        </Grid.RowDefinitions>
 | 
			
		||||
        <ContentControl Grid.Row="0" Grid.RowSpan="2" Grid.Column="0" Panel.ZIndex="2" Name="Icon" Margin="10" Width="25" />
 | 
			
		||||
        <ProgressBar Grid.Row="0" Grid.RowSpan="2" Grid.Column="0" Grid.ColumnSpan="2" Panel.ZIndex="1" Name="Progress" BorderThickness="0" />
 | 
			
		||||
        <TextBlock Grid.Row="0" Grid.Column="1" Panel.ZIndex="2" Name="ItemName" FontWeight="Bold" Margin="0,10,10,0" />
 | 
			
		||||
        <TextBlock Grid.Row="1" Grid.Column="1" Panel.ZIndex="2"  Name="Status" FontStyle="Italic" Margin="0,0,10,10" />
 | 
			
		||||
    </Grid>
 | 
			
		||||
</UserControl>
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,56 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright (c) 2020 ETH Zürich, Educational Development and Technology (LET)
 | 
			
		||||
 * 
 | 
			
		||||
 * This Source Code Form is subject to the terms of the Mozilla Public
 | 
			
		||||
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 | 
			
		||||
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
using System;
 | 
			
		||||
using System.IO;
 | 
			
		||||
using System.Windows.Controls;
 | 
			
		||||
using SafeExamBrowser.I18n.Contracts;
 | 
			
		||||
using SafeExamBrowser.UserInterface.Contracts.Browser.Data;
 | 
			
		||||
using SafeExamBrowser.UserInterface.Shared.Utilities;
 | 
			
		||||
 | 
			
		||||
namespace SafeExamBrowser.UserInterface.Desktop.Controls.Browser
 | 
			
		||||
{
 | 
			
		||||
	public partial class DownloadItemControl : UserControl
 | 
			
		||||
	{
 | 
			
		||||
		private IText text;
 | 
			
		||||
 | 
			
		||||
		public Guid Id { get; }
 | 
			
		||||
 | 
			
		||||
		public DownloadItemControl(Guid id, IText text)
 | 
			
		||||
		{
 | 
			
		||||
			this.Id = id;
 | 
			
		||||
			this.text = text;
 | 
			
		||||
 | 
			
		||||
			InitializeComponent();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public void Update(DownloadItemState state)
 | 
			
		||||
		{
 | 
			
		||||
			ItemName.Text = Uri.TryCreate(state.Url, UriKind.Absolute, out var uri) ? Path.GetFileName(uri.AbsolutePath) : state.Url;
 | 
			
		||||
			Progress.Value = state.Completion * 100;
 | 
			
		||||
			Status.Text = $"{text.Get(TextKey.BrowserWindow_Downloading)} ({state.Completion * 100}%)";
 | 
			
		||||
 | 
			
		||||
			if (File.Exists(state.FullPath))
 | 
			
		||||
			{
 | 
			
		||||
				ItemName.Text = Path.GetFileName(state.FullPath);
 | 
			
		||||
				Icon.Content = new Image { Source = IconLoader.LoadIconFor(new FileInfo(state.FullPath)) };
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if (state.IsCancelled)
 | 
			
		||||
			{
 | 
			
		||||
				Progress.Visibility = System.Windows.Visibility.Collapsed;
 | 
			
		||||
				Status.Text = text.Get(TextKey.BrowserWindow_DownloadCancelled);
 | 
			
		||||
			}
 | 
			
		||||
			else if (state.IsComplete)
 | 
			
		||||
			{
 | 
			
		||||
				Progress.Visibility = System.Windows.Visibility.Collapsed;
 | 
			
		||||
				Status.Text = text.Get(TextKey.BrowserWindow_DownloadComplete);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -5,7 +5,7 @@
 | 
			
		|||
        xmlns:fa="http://schemas.fontawesome.io/icons/"
 | 
			
		||||
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
 | 
			
		||||
        xmlns:local="clr-namespace:SafeExamBrowser.UserInterface.Desktop"
 | 
			
		||||
        mc:Ignorable="d" Height="500" Width="750" ResizeMode="NoResize">
 | 
			
		||||
        mc:Ignorable="d" Height="500" Width="750" ResizeMode="NoResize" WindowStartupLocation="CenterScreen">
 | 
			
		||||
    <Window.Resources>
 | 
			
		||||
        <ResourceDictionary>
 | 
			
		||||
            <ResourceDictionary.MergedDictionaries>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -22,12 +22,13 @@ using SafeExamBrowser.UserInterface.Shared.Utilities;
 | 
			
		|||
 | 
			
		||||
namespace SafeExamBrowser.UserInterface.Desktop
 | 
			
		||||
{
 | 
			
		||||
	public partial class FileSystemDialog : Window, IFileSystemDialog
 | 
			
		||||
	public partial class FileSystemDialog : Window
 | 
			
		||||
	{
 | 
			
		||||
		private FileSystemElement element;
 | 
			
		||||
		private string initialPath;
 | 
			
		||||
		private string message;
 | 
			
		||||
		private FileSystemOperation operation;
 | 
			
		||||
		private IWindow parent;
 | 
			
		||||
		private IText text;
 | 
			
		||||
		private string title;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -37,12 +38,14 @@ namespace SafeExamBrowser.UserInterface.Desktop
 | 
			
		|||
			FileSystemOperation operation,
 | 
			
		||||
			IText text,
 | 
			
		||||
			string message = default(string),
 | 
			
		||||
			string title = default(string))
 | 
			
		||||
			string title = default(string),
 | 
			
		||||
			IWindow parent = default(IWindow))
 | 
			
		||||
		{
 | 
			
		||||
			this.element = element;
 | 
			
		||||
			this.initialPath = initialPath;
 | 
			
		||||
			this.message = message;
 | 
			
		||||
			this.operation = operation;
 | 
			
		||||
			this.parent = parent;
 | 
			
		||||
			this.text = text;
 | 
			
		||||
			this.title = title;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -50,26 +53,23 @@ namespace SafeExamBrowser.UserInterface.Desktop
 | 
			
		|||
			InitializeDialog();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public FileSystemDialogResult Show(IWindow parent = null)
 | 
			
		||||
		internal new FileSystemDialogResult Show()
 | 
			
		||||
		{
 | 
			
		||||
			return Dispatcher.Invoke(() =>
 | 
			
		||||
			var result = new FileSystemDialogResult();
 | 
			
		||||
 | 
			
		||||
			if (parent is Window)
 | 
			
		||||
			{
 | 
			
		||||
				var result = new FileSystemDialogResult();
 | 
			
		||||
				Owner = parent as Window;
 | 
			
		||||
				WindowStartupLocation = WindowStartupLocation.CenterOwner;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
				if (parent is Window)
 | 
			
		||||
				{
 | 
			
		||||
					Owner = parent as Window;
 | 
			
		||||
					WindowStartupLocation = WindowStartupLocation.CenterOwner;
 | 
			
		||||
				}
 | 
			
		||||
			if (ShowDialog() == true)
 | 
			
		||||
			{
 | 
			
		||||
				result.FullPath = BuildFullPath();
 | 
			
		||||
				result.Success = true;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
				if (ShowDialog() == true)
 | 
			
		||||
				{
 | 
			
		||||
					result.FullPath = BuildFullPath();
 | 
			
		||||
					result.Success = true;
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				return result;
 | 
			
		||||
			});
 | 
			
		||||
			return result;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		private void CancelButton_Click(object sender, RoutedEventArgs e)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,37 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright (c) 2020 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 SafeExamBrowser.I18n.Contracts;
 | 
			
		||||
using SafeExamBrowser.UserInterface.Contracts.FileSystemDialog;
 | 
			
		||||
using SafeExamBrowser.UserInterface.Contracts.Windows;
 | 
			
		||||
 | 
			
		||||
namespace SafeExamBrowser.UserInterface.Desktop
 | 
			
		||||
{
 | 
			
		||||
	public class FileSystemDialogFactory : IFileSystemDialog
 | 
			
		||||
	{
 | 
			
		||||
		private IText text;
 | 
			
		||||
 | 
			
		||||
		public FileSystemDialogFactory(IText text)
 | 
			
		||||
		{
 | 
			
		||||
			this.text = text;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public FileSystemDialogResult Show(FileSystemElement element, string initialPath, FileSystemOperation operation, string message = null, string title = null, IWindow owner = null)
 | 
			
		||||
		{
 | 
			
		||||
			if (owner is Window window)
 | 
			
		||||
			{
 | 
			
		||||
				return window.Dispatcher.Invoke(() => new FileSystemDialog(element, initialPath, operation, text, message, title, owner).Show());
 | 
			
		||||
			}
 | 
			
		||||
			else
 | 
			
		||||
			{
 | 
			
		||||
				return new FileSystemDialog(element, initialPath, operation, text, message, title).Show();
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -109,6 +109,9 @@
 | 
			
		|||
    <Compile Include="Controls\ActionCenterWirelessNetworkControl.xaml.cs">
 | 
			
		||||
      <DependentUpon>ActionCenterWirelessNetworkControl.xaml</DependentUpon>
 | 
			
		||||
    </Compile>
 | 
			
		||||
    <Compile Include="Controls\Browser\DownloadItemControl.xaml.cs">
 | 
			
		||||
      <DependentUpon>DownloadItemControl.xaml</DependentUpon>
 | 
			
		||||
    </Compile>
 | 
			
		||||
    <Compile Include="Controls\TaskbarApplicationControl.xaml.cs">
 | 
			
		||||
      <DependentUpon>TaskbarApplicationControl.xaml</DependentUpon>
 | 
			
		||||
    </Compile>
 | 
			
		||||
| 
						 | 
				
			
			@ -148,6 +151,7 @@
 | 
			
		|||
    <Compile Include="FileSystemDialog.xaml.cs">
 | 
			
		||||
      <DependentUpon>FileSystemDialog.xaml</DependentUpon>
 | 
			
		||||
    </Compile>
 | 
			
		||||
    <Compile Include="FileSystemDialogFactory.cs" />
 | 
			
		||||
    <Compile Include="FolderDialog.cs" />
 | 
			
		||||
    <Compile Include="LockScreen.xaml.cs">
 | 
			
		||||
      <DependentUpon>LockScreen.xaml</DependentUpon>
 | 
			
		||||
| 
						 | 
				
			
			@ -229,6 +233,10 @@
 | 
			
		|||
      <SubType>Designer</SubType>
 | 
			
		||||
      <Generator>MSBuild:Compile</Generator>
 | 
			
		||||
    </Page>
 | 
			
		||||
    <Page Include="Controls\Browser\DownloadItemControl.xaml">
 | 
			
		||||
      <SubType>Designer</SubType>
 | 
			
		||||
      <Generator>MSBuild:Compile</Generator>
 | 
			
		||||
    </Page>
 | 
			
		||||
    <Page Include="Controls\TaskbarApplicationControl.xaml">
 | 
			
		||||
      <SubType>Designer</SubType>
 | 
			
		||||
      <Generator>MSBuild:Compile</Generator>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -23,7 +23,6 @@ using SafeExamBrowser.SystemComponents.Contracts.PowerSupply;
 | 
			
		|||
using SafeExamBrowser.SystemComponents.Contracts.WirelessNetwork;
 | 
			
		||||
using SafeExamBrowser.UserInterface.Contracts;
 | 
			
		||||
using SafeExamBrowser.UserInterface.Contracts.Browser;
 | 
			
		||||
using SafeExamBrowser.UserInterface.Contracts.FileSystemDialog;
 | 
			
		||||
using SafeExamBrowser.UserInterface.Contracts.Shell;
 | 
			
		||||
using SafeExamBrowser.UserInterface.Contracts.Windows;
 | 
			
		||||
using SafeExamBrowser.UserInterface.Contracts.Windows.Data;
 | 
			
		||||
| 
						 | 
				
			
			@ -76,11 +75,6 @@ namespace SafeExamBrowser.UserInterface.Desktop
 | 
			
		|||
			return Application.Current.Dispatcher.Invoke(() => new BrowserWindow(control, settings, isMainWindow, text));
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public IFileSystemDialog CreateFileSystemDialog(FileSystemElement element, string initialPath, FileSystemOperation operation, string message = default(string), string title = default(string))
 | 
			
		||||
		{
 | 
			
		||||
			return Application.Current.Dispatcher.Invoke(() => new FileSystemDialog(element, initialPath, operation, text, message, title));
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public IFolderDialog CreateFolderDialog(string message)
 | 
			
		||||
		{
 | 
			
		||||
			return new FolderDialog(message);
 | 
			
		||||
| 
						 | 
				
			
			@ -105,26 +99,26 @@ namespace SafeExamBrowser.UserInterface.Desktop
 | 
			
		|||
 | 
			
		||||
		public IWindow CreateLogWindow(ILogger logger)
 | 
			
		||||
		{
 | 
			
		||||
			LogWindow logWindow = null;
 | 
			
		||||
			var logWindowReadyEvent = new AutoResetEvent(false);
 | 
			
		||||
			var logWindowThread = new Thread(() =>
 | 
			
		||||
			var window = default(LogWindow);
 | 
			
		||||
			var windowReadyEvent = new AutoResetEvent(false);
 | 
			
		||||
			var windowThread = new Thread(() =>
 | 
			
		||||
			{
 | 
			
		||||
				logWindow = new LogWindow(logger, text);
 | 
			
		||||
				logWindow.Closed += (o, args) => logWindow.Dispatcher.InvokeShutdown();
 | 
			
		||||
				logWindow.Show();
 | 
			
		||||
				window = new LogWindow(logger, text);
 | 
			
		||||
				window.Closed += (o, args) => window.Dispatcher.InvokeShutdown();
 | 
			
		||||
				window.Show();
 | 
			
		||||
 | 
			
		||||
				logWindowReadyEvent.Set();
 | 
			
		||||
				windowReadyEvent.Set();
 | 
			
		||||
 | 
			
		||||
				System.Windows.Threading.Dispatcher.Run();
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
			logWindowThread.SetApartmentState(ApartmentState.STA);
 | 
			
		||||
			logWindowThread.IsBackground = true;
 | 
			
		||||
			logWindowThread.Start();
 | 
			
		||||
			windowThread.SetApartmentState(ApartmentState.STA);
 | 
			
		||||
			windowThread.IsBackground = true;
 | 
			
		||||
			windowThread.Start();
 | 
			
		||||
 | 
			
		||||
			logWindowReadyEvent.WaitOne();
 | 
			
		||||
			windowReadyEvent.WaitOne();
 | 
			
		||||
 | 
			
		||||
			return logWindow;
 | 
			
		||||
			return window;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public INotificationControl CreateNotificationControl(INotificationController controller, INotificationInfo info, Location location)
 | 
			
		||||
| 
						 | 
				
			
			@ -168,27 +162,26 @@ namespace SafeExamBrowser.UserInterface.Desktop
 | 
			
		|||
 | 
			
		||||
		public ISplashScreen CreateSplashScreen(AppConfig appConfig = null)
 | 
			
		||||
		{
 | 
			
		||||
			SplashScreen splashScreen = null;
 | 
			
		||||
			var splashReadyEvent = new AutoResetEvent(false);
 | 
			
		||||
			var splashScreenThread = new Thread(() =>
 | 
			
		||||
			var window = default(SplashScreen);
 | 
			
		||||
			var windowReadyEvent = new AutoResetEvent(false);
 | 
			
		||||
			var windowThread = new Thread(() =>
 | 
			
		||||
			{
 | 
			
		||||
				splashScreen = new SplashScreen(text, appConfig);
 | 
			
		||||
				splashScreen.Closed += (o, args) => splashScreen.Dispatcher.InvokeShutdown();
 | 
			
		||||
				splashScreen.Show();
 | 
			
		||||
				window = new SplashScreen(text, appConfig);
 | 
			
		||||
				window.Closed += (o, args) => window.Dispatcher.InvokeShutdown();
 | 
			
		||||
				window.Show();
 | 
			
		||||
 | 
			
		||||
				splashReadyEvent.Set();
 | 
			
		||||
				windowReadyEvent.Set();
 | 
			
		||||
 | 
			
		||||
				System.Windows.Threading.Dispatcher.Run();
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
			splashScreenThread.SetApartmentState(ApartmentState.STA);
 | 
			
		||||
			splashScreenThread.Name = nameof(SplashScreen);
 | 
			
		||||
			splashScreenThread.IsBackground = true;
 | 
			
		||||
			splashScreenThread.Start();
 | 
			
		||||
			windowThread.SetApartmentState(ApartmentState.STA);
 | 
			
		||||
			windowThread.IsBackground = true;
 | 
			
		||||
			windowThread.Start();
 | 
			
		||||
 | 
			
		||||
			splashReadyEvent.WaitOne();
 | 
			
		||||
			windowReadyEvent.WaitOne();
 | 
			
		||||
 | 
			
		||||
			return splashScreen;
 | 
			
		||||
			return window;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public ISystemControl CreateWirelessNetworkControl(IWirelessAdapter wirelessAdapter, Location location)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -33,12 +33,21 @@
 | 
			
		|||
                        <ColumnDefinition Width="Auto" />
 | 
			
		||||
                        <ColumnDefinition Width="*" />
 | 
			
		||||
                        <ColumnDefinition Width="Auto" />
 | 
			
		||||
                        <ColumnDefinition Width="Auto" />
 | 
			
		||||
                    </Grid.ColumnDefinitions>
 | 
			
		||||
                    <Button Grid.Column="0" x:Name="BackwardButton" Height="50" HorizontalAlignment="Center" Margin="5" Template="{StaticResource BrowserButton}" VerticalAlignment="Center" />
 | 
			
		||||
                    <Button Grid.Column="1" x:Name="ForwardButton" Height="50" HorizontalAlignment="Center" Margin="5" Template="{StaticResource BrowserButton}" VerticalAlignment="Center" />
 | 
			
		||||
                    <Button Grid.Column="2" x:Name="ReloadButton" Height="50" HorizontalAlignment="Center" Margin="5" Template="{StaticResource BrowserButton}" VerticalAlignment="Center" />
 | 
			
		||||
                    <TextBox Grid.Column="3" x:Name="UrlTextBox" Height="50" HorizontalAlignment="Stretch" Margin="5,10" Padding="8" VerticalContentAlignment="Center" />
 | 
			
		||||
                    <Button Grid.Column="4" x:Name="MenuButton" Height="50" HorizontalAlignment="Center" Margin="5" Template="{StaticResource BrowserButton}" VerticalAlignment="Center" />
 | 
			
		||||
                    <Button Grid.Column="4" x:Name="DownloadsButton" Height="50" HorizontalAlignment="Center" Margin="5" Padding="5" Template="{StaticResource BrowserButton}" VerticalAlignment="Center" Visibility="Collapsed">
 | 
			
		||||
                        <fa:ImageAwesome Icon="Download" />
 | 
			
		||||
                    </Button>
 | 
			
		||||
                    <Popup x:Name="DownloadsPopup" AllowsTransparency="True" PopupAnimation="Slide" Placement="Custom" PlacementTarget="{Binding ElementName=BrowserControlHost}">
 | 
			
		||||
                        <Border Background="{StaticResource BackgroundBrush}" BorderBrush="LightGray" BorderThickness="1,0,1,1" MinWidth="250">
 | 
			
		||||
                            <StackPanel x:Name="Downloads" Orientation="Vertical" />
 | 
			
		||||
                        </Border>
 | 
			
		||||
                    </Popup>
 | 
			
		||||
                    <Button Grid.Column="5" x:Name="MenuButton" Height="50" HorizontalAlignment="Center" Margin="5" Template="{StaticResource BrowserButton}" VerticalAlignment="Center" />
 | 
			
		||||
                    <Popup x:Name="MenuPopup" IsOpen="False" AllowsTransparency="True" PopupAnimation="Slide" Placement="Custom" PlacementTarget="{Binding ElementName=BrowserControlHost}">
 | 
			
		||||
                        <Border Background="{StaticResource BackgroundBrush}" BorderBrush="LightGray" BorderThickness="1,0,1,1" Width="350">
 | 
			
		||||
                            <StackPanel Orientation="Vertical">
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -13,16 +13,17 @@ using System.Windows;
 | 
			
		|||
using System.Windows.Controls.Primitives;
 | 
			
		||||
using System.Windows.Input;
 | 
			
		||||
using System.Windows.Interop;
 | 
			
		||||
using System.Windows.Media;
 | 
			
		||||
using System.Windows.Media.Imaging;
 | 
			
		||||
using SafeExamBrowser.Applications.Contracts.Resources.Icons;
 | 
			
		||||
using SafeExamBrowser.I18n.Contracts;
 | 
			
		||||
using SafeExamBrowser.Settings.Browser;
 | 
			
		||||
using SafeExamBrowser.UserInterface.Contracts;
 | 
			
		||||
using SafeExamBrowser.UserInterface.Contracts.Browser;
 | 
			
		||||
using SafeExamBrowser.UserInterface.Contracts.Browser.Data;
 | 
			
		||||
using SafeExamBrowser.UserInterface.Contracts.Browser.Events;
 | 
			
		||||
using SafeExamBrowser.UserInterface.Contracts.Windows;
 | 
			
		||||
using SafeExamBrowser.UserInterface.Contracts.Windows.Events;
 | 
			
		||||
using SafeExamBrowser.UserInterface.Mobile.Controls.Browser;
 | 
			
		||||
using SafeExamBrowser.UserInterface.Shared.Utilities;
 | 
			
		||||
 | 
			
		||||
namespace SafeExamBrowser.UserInterface.Mobile
 | 
			
		||||
| 
						 | 
				
			
			@ -117,6 +118,36 @@ namespace SafeExamBrowser.UserInterface.Mobile
 | 
			
		|||
			});
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public void UpdateDownloadState(DownloadItemState state)
 | 
			
		||||
		{
 | 
			
		||||
			Dispatcher.InvokeAsync(() =>
 | 
			
		||||
			{
 | 
			
		||||
				var isNewItem = true;
 | 
			
		||||
 | 
			
		||||
				foreach (var child in Downloads.Children)
 | 
			
		||||
				{
 | 
			
		||||
					if (child is DownloadItemControl control && control.Id == state.Id)
 | 
			
		||||
					{
 | 
			
		||||
						control.Update(state);
 | 
			
		||||
						isNewItem = false;
 | 
			
		||||
 | 
			
		||||
						break;
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				if (isNewItem)
 | 
			
		||||
				{
 | 
			
		||||
					var control = new DownloadItemControl(state.Id, text);
 | 
			
		||||
 | 
			
		||||
					control.Update(state);
 | 
			
		||||
					Downloads.Children.Add(control);
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				DownloadsButton.Visibility = Visibility.Visible;
 | 
			
		||||
				DownloadsPopup.IsOpen = IsActive;
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public void UpdateLoadingState(bool isLoading)
 | 
			
		||||
		{
 | 
			
		||||
			Dispatcher.Invoke(() => ProgressBar.Visibility = isLoading ? Visibility.Visible : Visibility.Hidden);
 | 
			
		||||
| 
						 | 
				
			
			@ -167,7 +198,7 @@ namespace SafeExamBrowser.UserInterface.Mobile
 | 
			
		|||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		private CustomPopupPlacement[] MenuPopup_PlacementCallback(Size popupSize, Size targetSize, Point offset)
 | 
			
		||||
		private CustomPopupPlacement[] Popup_PlacementCallback(Size popupSize, Size targetSize, Point offset)
 | 
			
		||||
		{
 | 
			
		||||
			return new[]
 | 
			
		||||
			{
 | 
			
		||||
| 
						 | 
				
			
			@ -216,21 +247,23 @@ namespace SafeExamBrowser.UserInterface.Mobile
 | 
			
		|||
 | 
			
		||||
		private void RegisterEvents()
 | 
			
		||||
		{
 | 
			
		||||
			var originalBrush = MenuButton.Background;
 | 
			
		||||
 | 
			
		||||
			BackwardButton.Click += (o, args) => BackwardNavigationRequested?.Invoke();
 | 
			
		||||
			Closing += BrowserWindow_Closing;
 | 
			
		||||
			DeveloperConsoleButton.Click += (o, args) => DeveloperConsoleRequested?.Invoke();
 | 
			
		||||
			DownloadsButton.Click += (o, args) => DownloadsPopup.IsOpen = !DownloadsPopup.IsOpen;
 | 
			
		||||
			DownloadsButton.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => DownloadsPopup.IsOpen = DownloadsPopup.IsMouseOver));
 | 
			
		||||
			DownloadsPopup.CustomPopupPlacementCallback = new CustomPopupPlacementCallback(Popup_PlacementCallback);
 | 
			
		||||
			DownloadsPopup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => DownloadsPopup.IsOpen = DownloadsPopup.IsMouseOver));
 | 
			
		||||
			ForwardButton.Click += (o, args) => ForwardNavigationRequested?.Invoke();
 | 
			
		||||
			Loaded += BrowserWindow_Loaded;
 | 
			
		||||
			MenuButton.Click += (o, args) => MenuPopup.IsOpen = !MenuPopup.IsOpen;
 | 
			
		||||
			MenuButton.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => MenuPopup.IsOpen = MenuPopup.IsMouseOver));
 | 
			
		||||
			MenuPopup.CustomPopupPlacementCallback = new CustomPopupPlacementCallback(MenuPopup_PlacementCallback);
 | 
			
		||||
			MenuPopup.Closed += (o, args) => MenuButton.Background = originalBrush;
 | 
			
		||||
			MenuPopup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => MenuPopup.IsOpen = IsMouseOver));
 | 
			
		||||
			MenuPopup.Opened += (o, args) => MenuButton.Background = Brushes.LightGray;
 | 
			
		||||
			MenuPopup.CustomPopupPlacementCallback = new CustomPopupPlacementCallback(Popup_PlacementCallback);
 | 
			
		||||
			MenuPopup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => MenuPopup.IsOpen = MenuPopup.IsMouseOver));
 | 
			
		||||
			KeyUp += BrowserWindow_KeyUp;
 | 
			
		||||
			LocationChanged += (o, args) => { DownloadsPopup.IsOpen = false; MenuPopup.IsOpen = false; };
 | 
			
		||||
			ReloadButton.Click += (o, args) => ReloadRequested?.Invoke();
 | 
			
		||||
			SizeChanged += (o, args) => { DownloadsPopup.IsOpen = false; MenuPopup.IsOpen = false; };
 | 
			
		||||
			SystemParameters.StaticPropertyChanged += SystemParameters_StaticPropertyChanged;
 | 
			
		||||
			UrlTextBox.GotKeyboardFocus += (o, args) => UrlTextBox.SelectAll();
 | 
			
		||||
			UrlTextBox.GotMouseCapture += UrlTextBox_GotMouseCapture;
 | 
			
		||||
| 
						 | 
				
			
			@ -270,6 +303,7 @@ namespace SafeExamBrowser.UserInterface.Mobile
 | 
			
		|||
				ForwardButton.Height = 35;
 | 
			
		||||
				ReloadButton.Height = 35;
 | 
			
		||||
				UrlTextBox.Height = 20;
 | 
			
		||||
				DownloadsButton.Height = 35;
 | 
			
		||||
				MenuButton.Height = 35;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,22 @@
 | 
			
		|||
<UserControl x:Class="SafeExamBrowser.UserInterface.Mobile.Controls.Browser.DownloadItemControl"
 | 
			
		||||
             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.Mobile.Controls.Browser"
 | 
			
		||||
             mc:Ignorable="d" d:DesignHeight="50" d:DesignWidth="200">
 | 
			
		||||
    <Grid>
 | 
			
		||||
        <Grid.ColumnDefinitions>
 | 
			
		||||
            <ColumnDefinition Width="Auto" />
 | 
			
		||||
            <ColumnDefinition Width="*" />
 | 
			
		||||
        </Grid.ColumnDefinitions>
 | 
			
		||||
        <Grid.RowDefinitions>
 | 
			
		||||
            <RowDefinition Height="*" />
 | 
			
		||||
            <RowDefinition Height="*" />
 | 
			
		||||
        </Grid.RowDefinitions>
 | 
			
		||||
        <ContentControl Grid.Row="0" Grid.RowSpan="2" Grid.Column="0" Panel.ZIndex="2" Name="Icon" Margin="10" Width="25" />
 | 
			
		||||
        <ProgressBar Grid.Row="0" Grid.RowSpan="2" Grid.Column="0" Grid.ColumnSpan="2" Panel.ZIndex="1" Name="Progress" BorderThickness="0" />
 | 
			
		||||
        <TextBlock Grid.Row="0" Grid.Column="1" Panel.ZIndex="2" Name="ItemName" FontWeight="Bold" Margin="0,10,10,0" />
 | 
			
		||||
        <TextBlock Grid.Row="1" Grid.Column="1" Panel.ZIndex="2"  Name="Status" FontStyle="Italic" Margin="0,0,10,10" />
 | 
			
		||||
    </Grid>
 | 
			
		||||
</UserControl>
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,56 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright (c) 2020 ETH Zürich, Educational Development and Technology (LET)
 | 
			
		||||
 * 
 | 
			
		||||
 * This Source Code Form is subject to the terms of the Mozilla Public
 | 
			
		||||
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 | 
			
		||||
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
using System;
 | 
			
		||||
using System.IO;
 | 
			
		||||
using System.Windows.Controls;
 | 
			
		||||
using SafeExamBrowser.I18n.Contracts;
 | 
			
		||||
using SafeExamBrowser.UserInterface.Contracts.Browser.Data;
 | 
			
		||||
using SafeExamBrowser.UserInterface.Shared.Utilities;
 | 
			
		||||
 | 
			
		||||
namespace SafeExamBrowser.UserInterface.Mobile.Controls.Browser
 | 
			
		||||
{
 | 
			
		||||
	public partial class DownloadItemControl : UserControl
 | 
			
		||||
	{
 | 
			
		||||
		private IText text;
 | 
			
		||||
 | 
			
		||||
		public Guid Id { get; }
 | 
			
		||||
 | 
			
		||||
		public DownloadItemControl(Guid id, IText text)
 | 
			
		||||
		{
 | 
			
		||||
			this.Id = id;
 | 
			
		||||
			this.text = text;
 | 
			
		||||
 | 
			
		||||
			InitializeComponent();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public void Update(DownloadItemState state)
 | 
			
		||||
		{
 | 
			
		||||
			ItemName.Text = Uri.TryCreate(state.Url, UriKind.Absolute, out var uri) ? Path.GetFileName(uri.AbsolutePath) : state.Url;
 | 
			
		||||
			Progress.Value = state.Completion * 100;
 | 
			
		||||
			Status.Text = $"{text.Get(TextKey.BrowserWindow_Downloading)} ({state.Completion * 100}%)";
 | 
			
		||||
 | 
			
		||||
			if (File.Exists(state.FullPath))
 | 
			
		||||
			{
 | 
			
		||||
				ItemName.Text = Path.GetFileName(state.FullPath);
 | 
			
		||||
				Icon.Content = new Image { Source = IconLoader.LoadIconFor(new FileInfo(state.FullPath)) };
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if (state.IsCancelled)
 | 
			
		||||
			{
 | 
			
		||||
				Progress.Visibility = System.Windows.Visibility.Collapsed;
 | 
			
		||||
				Status.Text = text.Get(TextKey.BrowserWindow_DownloadCancelled);
 | 
			
		||||
			}
 | 
			
		||||
			else if (state.IsComplete)
 | 
			
		||||
			{
 | 
			
		||||
				Progress.Visibility = System.Windows.Visibility.Collapsed;
 | 
			
		||||
				Status.Text = text.Get(TextKey.BrowserWindow_DownloadComplete);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -22,7 +22,7 @@ using SafeExamBrowser.UserInterface.Shared.Utilities;
 | 
			
		|||
 | 
			
		||||
namespace SafeExamBrowser.UserInterface.Mobile
 | 
			
		||||
{
 | 
			
		||||
	public partial class FileSystemDialog : Window, IFileSystemDialog
 | 
			
		||||
	public partial class FileSystemDialog : Window
 | 
			
		||||
	{
 | 
			
		||||
		private FileSystemElement element;
 | 
			
		||||
		private string initialPath;
 | 
			
		||||
| 
						 | 
				
			
			@ -30,6 +30,7 @@ namespace SafeExamBrowser.UserInterface.Mobile
 | 
			
		|||
		private FileSystemOperation operation;
 | 
			
		||||
		private IText text;
 | 
			
		||||
		private string title;
 | 
			
		||||
		private IWindow parent;
 | 
			
		||||
 | 
			
		||||
		public FileSystemDialog(
 | 
			
		||||
			FileSystemElement element,
 | 
			
		||||
| 
						 | 
				
			
			@ -37,12 +38,14 @@ namespace SafeExamBrowser.UserInterface.Mobile
 | 
			
		|||
			FileSystemOperation operation,
 | 
			
		||||
			IText text,
 | 
			
		||||
			string message = default(string),
 | 
			
		||||
			string title = default(string))
 | 
			
		||||
			string title = default(string),
 | 
			
		||||
			IWindow parent = default(IWindow))
 | 
			
		||||
		{
 | 
			
		||||
			this.element = element;
 | 
			
		||||
			this.initialPath = initialPath;
 | 
			
		||||
			this.message = message;
 | 
			
		||||
			this.operation = operation;
 | 
			
		||||
			this.parent = parent;
 | 
			
		||||
			this.text = text;
 | 
			
		||||
			this.title = title;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -50,26 +53,23 @@ namespace SafeExamBrowser.UserInterface.Mobile
 | 
			
		|||
			InitializeDialog();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public FileSystemDialogResult Show(IWindow parent = null)
 | 
			
		||||
		internal new FileSystemDialogResult Show()
 | 
			
		||||
		{
 | 
			
		||||
			return Dispatcher.Invoke(() =>
 | 
			
		||||
			var result = new FileSystemDialogResult();
 | 
			
		||||
 | 
			
		||||
			if (parent is Window)
 | 
			
		||||
			{
 | 
			
		||||
				var result = new FileSystemDialogResult();
 | 
			
		||||
				Owner = parent as Window;
 | 
			
		||||
				WindowStartupLocation = WindowStartupLocation.CenterOwner;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
				if (parent is Window)
 | 
			
		||||
				{
 | 
			
		||||
					Owner = parent as Window;
 | 
			
		||||
					WindowStartupLocation = WindowStartupLocation.CenterOwner;
 | 
			
		||||
				}
 | 
			
		||||
			if (ShowDialog() == true)
 | 
			
		||||
			{
 | 
			
		||||
				result.FullPath = BuildFullPath();
 | 
			
		||||
				result.Success = true;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
				if (ShowDialog() == true)
 | 
			
		||||
				{
 | 
			
		||||
					result.FullPath = BuildFullPath();
 | 
			
		||||
					result.Success = true;
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				return result;
 | 
			
		||||
			});
 | 
			
		||||
			return result;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		private void CancelButton_Click(object sender, RoutedEventArgs e)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,37 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright (c) 2020 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 SafeExamBrowser.I18n.Contracts;
 | 
			
		||||
using SafeExamBrowser.UserInterface.Contracts.FileSystemDialog;
 | 
			
		||||
using SafeExamBrowser.UserInterface.Contracts.Windows;
 | 
			
		||||
 | 
			
		||||
namespace SafeExamBrowser.UserInterface.Mobile
 | 
			
		||||
{
 | 
			
		||||
	public class FileSystemDialogFactory : IFileSystemDialog
 | 
			
		||||
	{
 | 
			
		||||
		private IText text;
 | 
			
		||||
 | 
			
		||||
		public FileSystemDialogFactory(IText text)
 | 
			
		||||
		{
 | 
			
		||||
			this.text = text;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public FileSystemDialogResult Show(FileSystemElement element, string initialPath, FileSystemOperation operation, string message = null, string title = null, IWindow owner = null)
 | 
			
		||||
		{
 | 
			
		||||
			if (owner is Window window)
 | 
			
		||||
			{
 | 
			
		||||
				return window.Dispatcher.Invoke(() => new FileSystemDialog(element, initialPath, operation, text, message, title, owner).Show());
 | 
			
		||||
			}
 | 
			
		||||
			else
 | 
			
		||||
			{
 | 
			
		||||
				return new FileSystemDialog(element, initialPath, operation, text, message, title).Show();
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -110,6 +110,9 @@
 | 
			
		|||
    <Compile Include="Controls\ActionCenterWirelessNetworkControl.xaml.cs">
 | 
			
		||||
      <DependentUpon>ActionCenterWirelessNetworkControl.xaml</DependentUpon>
 | 
			
		||||
    </Compile>
 | 
			
		||||
    <Compile Include="Controls\Browser\DownloadItemControl.xaml.cs">
 | 
			
		||||
      <DependentUpon>DownloadItemControl.xaml</DependentUpon>
 | 
			
		||||
    </Compile>
 | 
			
		||||
    <Compile Include="Controls\TaskbarApplicationControl.xaml.cs">
 | 
			
		||||
      <DependentUpon>TaskbarApplicationControl.xaml</DependentUpon>
 | 
			
		||||
    </Compile>
 | 
			
		||||
| 
						 | 
				
			
			@ -149,6 +152,7 @@
 | 
			
		|||
    <Compile Include="FileSystemDialog.xaml.cs">
 | 
			
		||||
      <DependentUpon>FileSystemDialog.xaml</DependentUpon>
 | 
			
		||||
    </Compile>
 | 
			
		||||
    <Compile Include="FileSystemDialogFactory.cs" />
 | 
			
		||||
    <Compile Include="FolderDialog.cs" />
 | 
			
		||||
    <Compile Include="LockScreen.xaml.cs">
 | 
			
		||||
      <DependentUpon>LockScreen.xaml</DependentUpon>
 | 
			
		||||
| 
						 | 
				
			
			@ -279,6 +283,10 @@
 | 
			
		|||
      <Generator>MSBuild:Compile</Generator>
 | 
			
		||||
      <SubType>Designer</SubType>
 | 
			
		||||
    </Page>
 | 
			
		||||
    <Page Include="Controls\Browser\DownloadItemControl.xaml">
 | 
			
		||||
      <Generator>MSBuild:Compile</Generator>
 | 
			
		||||
      <SubType>Designer</SubType>
 | 
			
		||||
    </Page>
 | 
			
		||||
    <Page Include="Controls\TaskbarApplicationControl.xaml">
 | 
			
		||||
      <Generator>MSBuild:Compile</Generator>
 | 
			
		||||
      <SubType>Designer</SubType>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -23,7 +23,6 @@ using SafeExamBrowser.SystemComponents.Contracts.PowerSupply;
 | 
			
		|||
using SafeExamBrowser.SystemComponents.Contracts.WirelessNetwork;
 | 
			
		||||
using SafeExamBrowser.UserInterface.Contracts;
 | 
			
		||||
using SafeExamBrowser.UserInterface.Contracts.Browser;
 | 
			
		||||
using SafeExamBrowser.UserInterface.Contracts.FileSystemDialog;
 | 
			
		||||
using SafeExamBrowser.UserInterface.Contracts.Shell;
 | 
			
		||||
using SafeExamBrowser.UserInterface.Contracts.Windows;
 | 
			
		||||
using SafeExamBrowser.UserInterface.Contracts.Windows.Data;
 | 
			
		||||
| 
						 | 
				
			
			@ -76,11 +75,6 @@ namespace SafeExamBrowser.UserInterface.Mobile
 | 
			
		|||
			return Application.Current.Dispatcher.Invoke(() => new BrowserWindow(control, settings, isMainWindow, text));
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public IFileSystemDialog CreateFileSystemDialog(FileSystemElement element, string initialPath, FileSystemOperation operation, string message = default(string), string title = default(string))
 | 
			
		||||
		{
 | 
			
		||||
			return Application.Current.Dispatcher.Invoke(() => new FileSystemDialog(element, initialPath, operation, text, message, title));
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public IFolderDialog CreateFolderDialog(string message)
 | 
			
		||||
		{
 | 
			
		||||
			return new FolderDialog(message);
 | 
			
		||||
| 
						 | 
				
			
			@ -105,26 +99,26 @@ namespace SafeExamBrowser.UserInterface.Mobile
 | 
			
		|||
 | 
			
		||||
		public IWindow CreateLogWindow(ILogger logger)
 | 
			
		||||
		{
 | 
			
		||||
			LogWindow logWindow = null;
 | 
			
		||||
			var logWindowReadyEvent = new AutoResetEvent(false);
 | 
			
		||||
			var logWindowThread = new Thread(() =>
 | 
			
		||||
			var window = default(LogWindow);
 | 
			
		||||
			var windowReadyEvent = new AutoResetEvent(false);
 | 
			
		||||
			var windowThread = new Thread(() =>
 | 
			
		||||
			{
 | 
			
		||||
				logWindow = new LogWindow(logger, text);
 | 
			
		||||
				logWindow.Closed += (o, args) => logWindow.Dispatcher.InvokeShutdown();
 | 
			
		||||
				logWindow.Show();
 | 
			
		||||
				window = new LogWindow(logger, text);
 | 
			
		||||
				window.Closed += (o, args) => window.Dispatcher.InvokeShutdown();
 | 
			
		||||
				window.Show();
 | 
			
		||||
 | 
			
		||||
				logWindowReadyEvent.Set();
 | 
			
		||||
				windowReadyEvent.Set();
 | 
			
		||||
 | 
			
		||||
				System.Windows.Threading.Dispatcher.Run();
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
			logWindowThread.SetApartmentState(ApartmentState.STA);
 | 
			
		||||
			logWindowThread.IsBackground = true;
 | 
			
		||||
			logWindowThread.Start();
 | 
			
		||||
			windowThread.SetApartmentState(ApartmentState.STA);
 | 
			
		||||
			windowThread.IsBackground = true;
 | 
			
		||||
			windowThread.Start();
 | 
			
		||||
 | 
			
		||||
			logWindowReadyEvent.WaitOne();
 | 
			
		||||
			windowReadyEvent.WaitOne();
 | 
			
		||||
 | 
			
		||||
			return logWindow;
 | 
			
		||||
			return window;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public INotificationControl CreateNotificationControl(INotificationController controller, INotificationInfo info, Location location)
 | 
			
		||||
| 
						 | 
				
			
			@ -168,27 +162,26 @@ namespace SafeExamBrowser.UserInterface.Mobile
 | 
			
		|||
 | 
			
		||||
		public ISplashScreen CreateSplashScreen(AppConfig appConfig = null)
 | 
			
		||||
		{
 | 
			
		||||
			SplashScreen splashScreen = null;
 | 
			
		||||
			var splashReadyEvent = new AutoResetEvent(false);
 | 
			
		||||
			var splashScreenThread = new Thread(() =>
 | 
			
		||||
			var window = default(SplashScreen);
 | 
			
		||||
			var windowReadyEvent = new AutoResetEvent(false);
 | 
			
		||||
			var windowThread = new Thread(() =>
 | 
			
		||||
			{
 | 
			
		||||
				splashScreen = new SplashScreen(text, appConfig);
 | 
			
		||||
				splashScreen.Closed += (o, args) => splashScreen.Dispatcher.InvokeShutdown();
 | 
			
		||||
				splashScreen.Show();
 | 
			
		||||
				window = new SplashScreen(text, appConfig);
 | 
			
		||||
				window.Closed += (o, args) => window.Dispatcher.InvokeShutdown();
 | 
			
		||||
				window.Show();
 | 
			
		||||
 | 
			
		||||
				splashReadyEvent.Set();
 | 
			
		||||
				windowReadyEvent.Set();
 | 
			
		||||
 | 
			
		||||
				System.Windows.Threading.Dispatcher.Run();
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
			splashScreenThread.SetApartmentState(ApartmentState.STA);
 | 
			
		||||
			splashScreenThread.Name = nameof(SplashScreen);
 | 
			
		||||
			splashScreenThread.IsBackground = true;
 | 
			
		||||
			splashScreenThread.Start();
 | 
			
		||||
			windowThread.SetApartmentState(ApartmentState.STA);
 | 
			
		||||
			windowThread.IsBackground = true;
 | 
			
		||||
			windowThread.Start();
 | 
			
		||||
 | 
			
		||||
			splashReadyEvent.WaitOne();
 | 
			
		||||
			windowReadyEvent.WaitOne();
 | 
			
		||||
 | 
			
		||||
			return splashScreen;
 | 
			
		||||
			return window;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public ISystemControl CreateWirelessNetworkControl(IWirelessAdapter wirelessAdapter, Location location)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		
		Reference in a new issue