diff --git a/SafeExamBrowser.Client/CompositionRoot.cs b/SafeExamBrowser.Client/CompositionRoot.cs index 953f1630..a11ee56b 100644 --- a/SafeExamBrowser.Client/CompositionRoot.cs +++ b/SafeExamBrowser.Client/CompositionRoot.cs @@ -309,8 +309,7 @@ namespace SafeExamBrowser.Client switch (uiMode) { case UserInterfaceMode.Mobile: - // TODO - throw new NotImplementedException(); + return new Mobile.TaskView(); default: return new Desktop.TaskView(); } diff --git a/SafeExamBrowser.UserInterface.Desktop/Controls/TaskViewInstanceControl.xaml b/SafeExamBrowser.UserInterface.Desktop/Controls/TaskViewInstanceControl.xaml index 1af0d30e..ebccd149 100644 --- a/SafeExamBrowser.UserInterface.Desktop/Controls/TaskViewInstanceControl.xaml +++ b/SafeExamBrowser.UserInterface.Desktop/Controls/TaskViewInstanceControl.xaml @@ -12,7 +12,7 @@ - + diff --git a/SafeExamBrowser.UserInterface.Desktop/Controls/TaskViewInstanceControl.xaml.cs b/SafeExamBrowser.UserInterface.Desktop/Controls/TaskViewInstanceControl.xaml.cs index f1997763..34647b3e 100644 --- a/SafeExamBrowser.UserInterface.Desktop/Controls/TaskViewInstanceControl.xaml.cs +++ b/SafeExamBrowser.UserInterface.Desktop/Controls/TaskViewInstanceControl.xaml.cs @@ -35,15 +35,15 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls internal void Deselect() { - Border.Visibility = Visibility.Hidden; Icon.MaxWidth = 40; + Indicator.Visibility = Visibility.Hidden; Title.FontWeight = FontWeights.Normal; } internal void Select() { - Border.Visibility = Visibility.Visible; Icon.MaxWidth = 50; + Indicator.Visibility = Visibility.Visible; Title.FontWeight = FontWeights.SemiBold; } diff --git a/SafeExamBrowser.UserInterface.Mobile/ActionCenter.xaml b/SafeExamBrowser.UserInterface.Mobile/ActionCenter.xaml index 83088909..84ce5ea4 100644 --- a/SafeExamBrowser.UserInterface.Mobile/ActionCenter.xaml +++ b/SafeExamBrowser.UserInterface.Mobile/ActionCenter.xaml @@ -4,11 +4,12 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:SafeExamBrowser.UserInterface.Mobile.Controls" - mc:Ignorable="d" Title="ActionCenter" Height="1000" Width="400" Background="#EEF0F0F0" AllowsTransparency="True" FontSize="16" - WindowStyle="None" Topmost="True" ResizeMode="NoResize"> + mc:Ignorable="d" Title="ActionCenter" Height="1000" Width="400" Background="{DynamicResource BackgroundTransparentBrush}" + AllowsTransparency="True" FontSize="16" WindowStyle="None" Topmost="True" ResizeMode="NoResize"> + diff --git a/SafeExamBrowser.UserInterface.Mobile/Controls/ActionCenterApplicationControl.xaml b/SafeExamBrowser.UserInterface.Mobile/Controls/ActionCenterApplicationControl.xaml index ec278dbd..1172d550 100644 --- a/SafeExamBrowser.UserInterface.Mobile/Controls/ActionCenterApplicationControl.xaml +++ b/SafeExamBrowser.UserInterface.Mobile/Controls/ActionCenterApplicationControl.xaml @@ -20,8 +20,8 @@ - + - + diff --git a/SafeExamBrowser.UserInterface.Mobile/Controls/TaskViewInstanceControl.xaml b/SafeExamBrowser.UserInterface.Mobile/Controls/TaskViewInstanceControl.xaml new file mode 100644 index 00000000..d0f8a794 --- /dev/null +++ b/SafeExamBrowser.UserInterface.Mobile/Controls/TaskViewInstanceControl.xaml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + diff --git a/SafeExamBrowser.UserInterface.Mobile/Controls/TaskViewInstanceControl.xaml.cs b/SafeExamBrowser.UserInterface.Mobile/Controls/TaskViewInstanceControl.xaml.cs new file mode 100644 index 00000000..fd143e76 --- /dev/null +++ b/SafeExamBrowser.UserInterface.Mobile/Controls/TaskViewInstanceControl.xaml.cs @@ -0,0 +1,69 @@ +/* + * 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.Applications.Contracts; +using SafeExamBrowser.Core.Contracts; +using SafeExamBrowser.UserInterface.Shared.Utilities; + +namespace SafeExamBrowser.UserInterface.Mobile.Controls +{ + public partial class TaskViewInstanceControl : UserControl + { + private IApplicationInstance instance; + + internal InstanceIdentifier Id => instance.Id; + + public TaskViewInstanceControl(IApplicationInstance instance) + { + this.instance = instance; + + InitializeComponent(); + InitializeControl(); + } + + internal void Activate() + { + instance.Activate(); + } + + internal void Deselect() + { + Icon.MaxWidth = 40; + Indicator.Visibility = Visibility.Hidden; + Title.FontWeight = FontWeights.Normal; + } + + internal void Select() + { + Icon.MaxWidth = 50; + Indicator.Visibility = Visibility.Visible; + Title.FontWeight = FontWeights.SemiBold; + } + + private void InitializeControl() + { + Icon.Content = IconResourceLoader.Load(instance.Icon); + Title.Text = instance.Name; + + instance.IconChanged += Instance_IconChanged; + instance.NameChanged += Instance_NameChanged; + } + + private void Instance_NameChanged(string name) + { + Dispatcher.InvokeAsync(() => Title.Text = name); + } + + private void Instance_IconChanged(IconResource icon) + { + Dispatcher.InvokeAsync(() => Icon.Content = IconResourceLoader.Load(instance.Icon)); + } + } +} diff --git a/SafeExamBrowser.UserInterface.Mobile/SafeExamBrowser.UserInterface.Mobile.csproj b/SafeExamBrowser.UserInterface.Mobile/SafeExamBrowser.UserInterface.Mobile.csproj index b2daab1e..75a7d088 100644 --- a/SafeExamBrowser.UserInterface.Mobile/SafeExamBrowser.UserInterface.Mobile.csproj +++ b/SafeExamBrowser.UserInterface.Mobile/SafeExamBrowser.UserInterface.Mobile.csproj @@ -143,6 +143,9 @@ TaskbarWirelessNetworkControl.xaml + + TaskViewInstanceControl.xaml + LockScreen.xaml @@ -168,6 +171,9 @@ Taskbar.xaml + + TaskView.xaml + @@ -353,6 +359,10 @@ MSBuild:Compile Designer + + MSBuild:Compile + Designer + MSBuild:Compile Designer @@ -457,6 +467,10 @@ MSBuild:Compile Designer + + MSBuild:Compile + Designer + Designer MSBuild:Compile diff --git a/SafeExamBrowser.UserInterface.Mobile/TaskView.xaml b/SafeExamBrowser.UserInterface.Mobile/TaskView.xaml new file mode 100644 index 00000000..5663312c --- /dev/null +++ b/SafeExamBrowser.UserInterface.Mobile/TaskView.xaml @@ -0,0 +1,19 @@ + + + + + + + + + + + + diff --git a/SafeExamBrowser.UserInterface.Mobile/TaskView.xaml.cs b/SafeExamBrowser.UserInterface.Mobile/TaskView.xaml.cs new file mode 100644 index 00000000..d577e7ea --- /dev/null +++ b/SafeExamBrowser.UserInterface.Mobile/TaskView.xaml.cs @@ -0,0 +1,167 @@ +/* + * 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; +using System.Collections.Generic; +using System.Linq; +using System.Windows; +using System.Windows.Controls; +using SafeExamBrowser.Applications.Contracts; +using SafeExamBrowser.UserInterface.Contracts.Shell; +using SafeExamBrowser.UserInterface.Mobile.Controls; + +namespace SafeExamBrowser.UserInterface.Mobile +{ + public partial class TaskView : Window, ITaskView + { + private LinkedListNode current; + private LinkedList controls; + private List instances; + + public TaskView() + { + controls = new LinkedList(); + instances = new List(); + + InitializeComponent(); + } + + public void Add(IApplication application) + { + application.InstanceStarted += Application_InstanceStarted; + } + + public void Register(ITaskViewActivator activator) + { + activator.Deactivated += Activator_Deactivated; + activator.NextActivated += Activator_Next; + activator.PreviousActivated += Activator_Previous; + } + + private void Application_InstanceStarted(IApplicationInstance instance) + { + Dispatcher.InvokeAsync(() => Add(instance)); + } + + private void Activator_Deactivated() + { + Dispatcher.InvokeAsync(ActivateAndHide); + } + + private void Activator_Next() + { + Dispatcher.InvokeAsync(SelectNext); + } + + private void Activator_Previous() + { + Dispatcher.InvokeAsync(SelectPrevious); + } + + private void Instance_Terminated(InstanceIdentifier id) + { + Dispatcher.InvokeAsync(() => Remove(id)); + } + + private void ActivateAndHide() + { + Activate(); + current?.Value.Activate(); + Hide(); + } + + private void Add(IApplicationInstance instance) + { + instance.Terminated += Instance_Terminated; + instances.Add(instance); + Update(); + } + + private void Remove(InstanceIdentifier id) + { + var instance = instances.FirstOrDefault(i => i.Id == id); + + if (instance != default(IApplicationInstance)) + { + instances.Remove(instance); + Update(); + } + } + + private void SelectNext() + { + ShowConditional(); + + if (current != null) + { + current.Value.Deselect(); + current = current.Next ?? controls.First; + current.Value.Select(); + } + } + + private void SelectPrevious() + { + ShowConditional(); + + if (current != null) + { + current.Value.Deselect(); + current = current.Previous ?? controls.Last; + current.Value.Select(); + } + } + + private void ShowConditional() + { + if (instances.Any() && Visibility != Visibility.Visible) + { + Show(); + Activate(); + } + } + + private void Update() + { + var max = Math.Ceiling(Math.Sqrt(instances.Count)); + var stack = new Stack(instances); + + controls.Clear(); + Rows.Children.Clear(); + + for (var rowCount = 0; rowCount < max && stack.Any(); rowCount++) + { + var row = new StackPanel { Orientation = Orientation.Horizontal, HorizontalAlignment = HorizontalAlignment.Center }; + + Rows.Children.Add(row); + + for (var columnIndex = 0; columnIndex < max && stack.Any(); columnIndex++) + { + var instance = stack.Pop(); + var control = new TaskViewInstanceControl(instance); + + controls.AddLast(control); + row.Children.Add(control); + } + } + + current = controls.First; + current?.Value.Select(); + + UpdateLayout(); + + Left = (SystemParameters.WorkArea.Width - Width) / 2 + SystemParameters.WorkArea.Left; + Top = (SystemParameters.WorkArea.Height - Height) / 2 + SystemParameters.WorkArea.Top; + + if (!instances.Any()) + { + Hide(); + } + } + } +} diff --git a/SafeExamBrowser.UserInterface.Mobile/Taskbar.xaml b/SafeExamBrowser.UserInterface.Mobile/Taskbar.xaml index e86741c6..a0b40401 100644 --- a/SafeExamBrowser.UserInterface.Mobile/Taskbar.xaml +++ b/SafeExamBrowser.UserInterface.Mobile/Taskbar.xaml @@ -5,11 +5,12 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:SafeExamBrowser.UserInterface.Mobile.Controls" xmlns:s="clr-namespace:System;assembly=mscorlib" - mc:Ignorable="d" Title="Taskbar" Background="#FFF0F0F0" Height="60" FontSize="16" Width="750" WindowStyle="None" Topmost="True" - ResizeMode="NoResize" Icon="./Images/SafeExamBrowser.ico"> + mc:Ignorable="d" Title="Taskbar" Background="{DynamicResource BackgroundBrush}" Height="60" FontSize="16" Width="750" WindowStyle="None" + Topmost="True" ResizeMode="NoResize" Icon="./Images/SafeExamBrowser.ico"> + @@ -27,7 +28,7 @@ - + diff --git a/SafeExamBrowser.UserInterface.Mobile/Templates/Colors.xaml b/SafeExamBrowser.UserInterface.Mobile/Templates/Colors.xaml index 468c0504..6a558e71 100644 --- a/SafeExamBrowser.UserInterface.Mobile/Templates/Colors.xaml +++ b/SafeExamBrowser.UserInterface.Mobile/Templates/Colors.xaml @@ -1,5 +1,9 @@  - #FFF0F0F0 #AA808080 + #FFF0F0F0 + #EEF0F0F0 + #99D3D3D3 + Black + DimGray \ No newline at end of file