Restore SEBPatch

This commit is contained in:
2025-06-01 11:44:20 +02:00
commit 8c656e3137
1297 changed files with 142172 additions and 0 deletions

View File

@@ -0,0 +1,24 @@
<UserControl x:Class="SafeExamBrowser.UserInterface.Mobile.Controls.ActionCenter.ApplicationButton" x:ClassModifier="internal"
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"
mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="../../Templates/Buttons.xaml" />
<ResourceDictionary Source="../../Templates/Colors.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid>
<Button x:Name="Button" Background="Transparent" Height="60" Padding="10" Template="{StaticResource ActionCenterButton}">
<StackPanel Orientation="Horizontal">
<ContentControl x:Name="Icon" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="0,0,10,0" Width="20" />
<TextBlock x:Name="Text" HorizontalAlignment="Left" VerticalAlignment="Center" Padding="5" MaxWidth="350" TextTrimming="CharacterEllipsis" />
</StackPanel>
</Button>
</Grid>
</UserControl>

View File

@@ -0,0 +1,69 @@
/*
* Copyright (c) 2024 ETH Zürich, IT Services
*
* 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.Windows.Automation;
using System.Windows.Controls;
using SafeExamBrowser.Applications.Contracts;
using SafeExamBrowser.Core.Contracts.Resources.Icons;
using SafeExamBrowser.UserInterface.Shared.Utilities;
namespace SafeExamBrowser.UserInterface.Mobile.Controls.ActionCenter
{
internal partial class ApplicationButton : UserControl
{
private readonly IApplication<IApplicationWindow> application;
private readonly IApplicationWindow window;
internal event EventHandler Clicked;
internal ApplicationButton(IApplication<IApplicationWindow> application, IApplicationWindow window = null)
{
this.application = application;
this.window = window;
InitializeComponent();
InitializeApplicationInstanceButton();
}
private void InitializeApplicationInstanceButton()
{
var tooltip = window?.Title ?? application.Tooltip;
Button.Click += (o, args) => Clicked?.Invoke(this, EventArgs.Empty);
Button.ToolTip = tooltip;
Icon.Content = IconResourceLoader.Load(window?.Icon ?? application.Icon);
Text.Text = window?.Title ?? application.Name;
if (tooltip != default)
{
AutomationProperties.SetName(Button, tooltip);
}
if (window != default)
{
window.IconChanged += Window_IconChanged;
window.TitleChanged += Window_TitleChanged;
}
}
private void Window_IconChanged(IconResource icon)
{
Dispatcher.InvokeAsync(() => Icon.Content = IconResourceLoader.Load(icon));
}
private void Window_TitleChanged(string title)
{
Dispatcher.InvokeAsync(() =>
{
Text.Text = title;
Button.ToolTip = title;
});
}
}
}

View File

@@ -0,0 +1,27 @@
<UserControl x:Class="SafeExamBrowser.UserInterface.Mobile.Controls.ActionCenter.ApplicationControl" x:ClassModifier="internal"
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"
mc:Ignorable="d" d:DesignHeight="250" d:DesignWidth="500">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="../../Templates/Buttons.xaml" />
<ResourceDictionary Source="../../Templates/Colors.xaml" />
<ResourceDictionary Source="../../Templates/ScrollViewers.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" x:Name="ApplicationName" Background="{StaticResource BackgroundTransparentEmphasisBrush}" FontWeight="Bold" Padding="5" TextAlignment="Center" />
<ContentControl Grid.Row="1" x:Name="ApplicationButton" FontWeight="Bold" />
<StackPanel Grid.Row="2" x:Name="WindowPanel" Background="{StaticResource BackgroundTransparentEmphasisBrush}" Orientation="Vertical" />
</Grid>
</UserControl>

View File

@@ -0,0 +1,70 @@
/*
* Copyright (c) 2024 ETH Zürich, IT Services
*
* 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.UserInterface.Contracts.Shell;
namespace SafeExamBrowser.UserInterface.Mobile.Controls.ActionCenter
{
internal partial class ApplicationControl : UserControl, IApplicationControl
{
private readonly IApplication<IApplicationWindow> application;
internal ApplicationControl(IApplication<IApplicationWindow> application)
{
this.application = application;
InitializeComponent();
InitializeApplicationControl();
}
private void InitializeApplicationControl()
{
var button = new ApplicationButton(application);
application.WindowsChanged += Application_WindowsChanged;
button.Clicked += (o, args) => application.Start();
ApplicationName.Text = application.Name;
ApplicationName.Visibility = Visibility.Collapsed;
ApplicationButton.Content = button;
}
private void Application_WindowsChanged()
{
Dispatcher.InvokeAsync(Update);
}
private void Update()
{
var windows = application.GetWindows();
WindowPanel.Children.Clear();
foreach (var window in windows)
{
var button = new ApplicationButton(application, window);
button.Clicked += (o, args) => window.Activate();
WindowPanel.Children.Add(button);
}
if (WindowPanel.Children.Count == 0)
{
ApplicationName.Visibility = Visibility.Collapsed;
ApplicationButton.Visibility = Visibility.Visible;
}
else
{
ApplicationName.Visibility = Visibility.Visible;
ApplicationButton.Visibility = Visibility.Collapsed;
}
}
}
}

View File

@@ -0,0 +1,49 @@
<UserControl x:Class="SafeExamBrowser.UserInterface.Mobile.Controls.ActionCenter.AudioControl" x:ClassModifier="internal"
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"
mc:Ignorable="d" d:DesignHeight="100" d:DesignWidth="125">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="../../Templates/Buttons.xaml" />
<ResourceDictionary Source="../../Templates/Colors.xaml" />
<ResourceDictionary Source="../../Templates/ScrollViewers.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid x:Name="Grid" Background="{StaticResource ActionCenterDarkBrush}" Height="82" Margin="2">
<Popup x:Name="Popup" IsOpen="False" Placement="Top" PlacementTarget="{Binding ElementName=Button}">
<Border Background="Gray">
<StackPanel Orientation="Vertical">
<TextBlock x:Name="AudioDeviceName" Foreground="White" Margin="5" TextAlignment="Center" />
<StackPanel Orientation="Horizontal" Height="60" Margin="5,0">
<Button x:Name="MuteButton" Background="Transparent" Foreground="White" Padding="5" Template="{StaticResource ActionCenterButton}" Width="60">
<ContentControl x:Name="PopupIcon" Focusable="False" />
</Button>
<Slider x:Name="Volume" Grid.Column="1" Orientation="Horizontal" TickFrequency="1" Maximum="100" IsSnapToTickEnabled="True"
IsMoveToPointEnabled="True" VerticalAlignment="Center" Width="125" Thumb.DragStarted="Volume_DragStarted" Thumb.DragCompleted="Volume_DragCompleted">
<Slider.LayoutTransform>
<ScaleTransform ScaleX="2" ScaleY="2" CenterX="0" CenterY="0"/>
</Slider.LayoutTransform>
</Slider>
<TextBlock Grid.Column="2" FontWeight="DemiBold" FontSize="20" Text="{Binding ElementName=Volume, Path=Value}"
TextAlignment="Center" VerticalAlignment="Center" Foreground="White" Width="60" />
</StackPanel>
</StackPanel>
</Border>
</Popup>
<Button x:Name="Button" Padding="2" Template="{StaticResource ActionCenterButton}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="2*" />
<RowDefinition Height="3*" />
</Grid.RowDefinitions>
<ContentControl x:Name="ButtonIcon" />
<TextBlock Grid.Row="1" x:Name="Text" FontSize="15" Foreground="White" TextAlignment="Center" TextTrimming="CharacterEllipsis" TextWrapping="Wrap" VerticalAlignment="Bottom" />
</Grid>
</Button>
</Grid>
</UserControl>

View File

@@ -0,0 +1,193 @@
/*
* Copyright (c) 2024 ETH Zürich, IT Services
*
* 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.Threading.Tasks;
using System.Windows;
using System.Windows.Automation;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Media;
using System.Windows.Threading;
using SafeExamBrowser.Core.Contracts.Resources.Icons;
using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.SystemComponents.Contracts.Audio;
using SafeExamBrowser.UserInterface.Contracts.Shell;
using SafeExamBrowser.UserInterface.Shared.Utilities;
namespace SafeExamBrowser.UserInterface.Mobile.Controls.ActionCenter
{
internal partial class AudioControl : UserControl, ISystemControl
{
private readonly IAudio audio;
private readonly IText text;
private bool muted;
private IconResource MutedIcon;
private IconResource NoDeviceIcon;
internal AudioControl(IAudio audio, IText text)
{
this.audio = audio;
this.text = text;
InitializeComponent();
InitializeAudioControl();
}
public void Close()
{
Popup.IsOpen = false;
}
private void InitializeAudioControl()
{
var originalBrush = Grid.Background;
audio.VolumeChanged += Audio_VolumeChanged;
Button.Click += (o, args) => Popup.IsOpen = !Popup.IsOpen;
var lastOpenedBySpacePress = false;
Button.PreviewKeyDown += (o, args) =>
{
// For some reason, the popup immediately closes again if opened by a Space Bar key event - as a mitigation,
// we record the space bar event and leave the popup open for at least 3 seconds.
if (args.Key == System.Windows.Input.Key.Space)
{
lastOpenedBySpacePress = true;
}
};
Button.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() =>
{
if (Popup.IsOpen && lastOpenedBySpacePress)
{
return;
}
Popup.IsOpen = Popup.IsMouseOver;
}));
MuteButton.Click += MuteButton_Click;
MutedIcon = new XamlIconResource { Uri = new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Mobile;component/Images/Audio_Muted.xaml") };
NoDeviceIcon = new XamlIconResource { Uri = new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Mobile;component/Images/Audio_Light_NoDevice.xaml") };
Popup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() =>
{
if (Popup.IsOpen && lastOpenedBySpacePress)
{
return;
}
Popup.IsOpen = IsMouseOver;
}));
Popup.Opened += (o, args) => Grid.Background = Brushes.Gray;
Popup.Closed += (o, args) =>
{
Grid.Background = originalBrush;
lastOpenedBySpacePress = false;
};
Volume.ValueChanged += Volume_ValueChanged;
if (audio.HasOutputDevice)
{
AudioDeviceName.Text = audio.DeviceFullName;
Button.IsEnabled = true;
UpdateVolume(audio.OutputVolume, audio.OutputMuted);
}
else
{
AudioDeviceName.Text = text.Get(TextKey.SystemControl_AudioDeviceNotFound);
Button.IsEnabled = false;
Button.ToolTip = text.Get(TextKey.SystemControl_AudioDeviceNotFound);
ButtonIcon.Content = IconResourceLoader.Load(NoDeviceIcon);
}
}
private void Audio_VolumeChanged(double volume, bool muted)
{
Dispatcher.InvokeAsync(() => UpdateVolume(volume, muted));
}
private void MuteButton_Click(object sender, RoutedEventArgs e)
{
if (muted)
{
audio.Unmute();
}
else
{
audio.Mute();
}
}
private void Volume_DragStarted(object sender, DragStartedEventArgs e)
{
Volume.ValueChanged -= Volume_ValueChanged;
}
private void Volume_DragCompleted(object sender, DragCompletedEventArgs e)
{
audio.SetVolume(Volume.Value / 100);
Volume.ValueChanged += Volume_ValueChanged;
}
private void Volume_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
audio.SetVolume(Volume.Value / 100);
}
private void UpdateVolume(double volume, bool muted)
{
var info = BuildInfoText(volume, muted);
this.muted = muted;
Button.ToolTip = info;
Text.Text = info;
Volume.ValueChanged -= Volume_ValueChanged;
Volume.Value = Math.Round(volume * 100);
Volume.ValueChanged += Volume_ValueChanged;
AutomationProperties.SetName(Button, info);
if (muted)
{
var tooltip = text.Get(TextKey.SystemControl_AudioDeviceUnmuteTooltip);
MuteButton.ToolTip = tooltip;
ButtonIcon.Content = IconResourceLoader.Load(MutedIcon);
PopupIcon.Content = IconResourceLoader.Load(MutedIcon);
AutomationProperties.SetName(MuteButton, tooltip);
}
else
{
var tooltip = text.Get(TextKey.SystemControl_AudioDeviceMuteTooltip);
MuteButton.ToolTip = tooltip;
ButtonIcon.Content = LoadIcon(volume);
PopupIcon.Content = LoadIcon(volume);
AutomationProperties.SetName(MuteButton, tooltip);
}
}
private string BuildInfoText(double volume, bool muted)
{
var info = text.Get(muted ? TextKey.SystemControl_AudioDeviceInfoMuted : TextKey.SystemControl_AudioDeviceInfo);
info = info.Replace("%%NAME%%", audio.DeviceShortName);
info = info.Replace("%%VOLUME%%", Convert.ToString(Math.Round(volume * 100)));
return info;
}
private UIElement LoadIcon(double volume)
{
var icon = volume > 0.66 ? "100" : (volume > 0.33 ? "66" : "33");
var uri = new Uri($"pack://application:,,,/SafeExamBrowser.UserInterface.Mobile;component/Images/Audio_Light_{icon}.xaml");
var resource = new XamlIconResource { Uri = uri };
return IconResourceLoader.Load(resource);
}
}
}

View File

@@ -0,0 +1,36 @@
<UserControl x:Class="SafeExamBrowser.UserInterface.Mobile.Controls.ActionCenter.Clock" x:ClassModifier="internal"
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:fa="http://schemas.fontawesome.io/icons/"
xmlns:local="clr-namespace:SafeExamBrowser.UserInterface.Mobile.Controls"
mc:Ignorable="d" d:DesignHeight="100" d:DesignWidth="125">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="../../Templates/Buttons.xaml" />
<ResourceDictionary Source="../../Templates/Colors.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid Background="{StaticResource ActionCenterDarkBrush}" Height="82" Margin="2" ToolTip="{Binding Path=ToolTip}">
<Button x:Name="Button" IsEnabled="False" Padding="2" Template="{StaticResource ActionCenterButton}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="2*" />
<RowDefinition Height="3*" />
</Grid.RowDefinitions>
<fa:ImageAwesome Grid.Row="0" Foreground="Black" Icon="ClockOutline" Margin="2" VerticalAlignment="Center" />
<Grid Grid.Row="1" Background="Transparent" Margin="5,0" VerticalAlignment="Bottom">
<Grid.RowDefinitions>
<RowDefinition Height="1*" />
<RowDefinition Height="1*" />
</Grid.RowDefinitions>
<TextBlock x:Name="TimeTextBlock" Grid.Row="0" Text="{Binding Path=Time}" FontSize="15" Foreground="White" HorizontalAlignment="Center" />
<TextBlock x:Name="DateTextBlock" Grid.Row="1" Text="{Binding Path=Date}" FontSize="15" Foreground="White" HorizontalAlignment="Center" />
</Grid>
</Grid>
</Button>
</Grid>
</UserControl>

View File

@@ -0,0 +1,32 @@
/*
* Copyright (c) 2024 ETH Zürich, IT Services
*
* 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.Controls;
using SafeExamBrowser.UserInterface.Mobile.ViewModels;
namespace SafeExamBrowser.UserInterface.Mobile.Controls.ActionCenter
{
internal partial class Clock : UserControl
{
private DateTimeViewModel model;
public Clock()
{
InitializeComponent();
InitializeControl();
}
private void InitializeControl()
{
model = new DateTimeViewModel(true);
DataContext = model;
TimeTextBlock.DataContext = model;
DateTextBlock.DataContext = model;
}
}
}

View File

@@ -0,0 +1,37 @@
<UserControl x:Class="SafeExamBrowser.UserInterface.Mobile.Controls.ActionCenter.KeyboardLayoutButton" x:ClassModifier="internal"
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:fa="http://schemas.fontawesome.io/icons/"
xmlns:local="clr-namespace:SafeExamBrowser.UserInterface.Mobile.Controls"
mc:Ignorable="d" d:DesignHeight="40" d:DesignWidth="250" IsTabStop="True">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="../../Templates/Buttons.xaml" />
<ResourceDictionary Source="../../Templates/Colors.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid>
<Button x:Name="Button" Background="Transparent" Height="60" Padding="10,0" Template="{StaticResource ActionCenterButton}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock x:Name="IsCurrentTextBlock" Grid.Column="0" FontSize="20" HorizontalAlignment="Center" VerticalAlignment="Center" Visibility="Hidden">•</TextBlock>
<TextBlock x:Name="CultureCodeTextBlock" Grid.Column="1" FontWeight="Bold" HorizontalAlignment="Left" Margin="10,0,5,0" VerticalAlignment="Center" />
<StackPanel Grid.Column="2" VerticalAlignment="Center">
<TextBlock x:Name="CultureNameTextBlock" Foreground="White" HorizontalAlignment="Left" Margin="5,0,10,0" TextDecorations="Underline" VerticalAlignment="Center" />
<StackPanel Orientation="Horizontal">
<fa:ImageAwesome Foreground="LightGray" Height="10" Icon="KeyboardOutline" Margin="5,0" />
<TextBlock x:Name="LayoutNameTextBlock" Foreground="LightGray" HorizontalAlignment="Left" Margin="0,0,10,0" VerticalAlignment="Center" />
</StackPanel>
</StackPanel>
</Grid>
</Button>
</Grid>
</UserControl>

View File

@@ -0,0 +1,51 @@
/*
* Copyright (c) 2024 ETH Zürich, IT Services
*
* 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.Windows;
using System.Windows.Automation;
using System.Windows.Controls;
using SafeExamBrowser.SystemComponents.Contracts.Keyboard;
namespace SafeExamBrowser.UserInterface.Mobile.Controls.ActionCenter
{
internal partial class KeyboardLayoutButton : UserControl
{
private readonly IKeyboardLayout layout;
internal bool IsCurrent
{
set { IsCurrentTextBlock.Visibility = value ? Visibility.Visible : Visibility.Hidden; }
}
internal Guid LayoutId
{
get { return layout.Id; }
}
internal event EventHandler LayoutSelected;
internal KeyboardLayoutButton(IKeyboardLayout layout)
{
this.layout = layout;
InitializeComponent();
InitializeLayoutButton();
}
private void InitializeLayoutButton()
{
Button.Click += (o, args) => LayoutSelected?.Invoke(this, EventArgs.Empty);
CultureCodeTextBlock.Text = layout.CultureCode;
CultureNameTextBlock.Text = layout.CultureName;
LayoutNameTextBlock.Text = layout.LayoutName;
AutomationProperties.SetName(Button, layout.CultureName);
}
}
}

View File

@@ -0,0 +1,37 @@
<UserControl x:Class="SafeExamBrowser.UserInterface.Mobile.Controls.ActionCenter.KeyboardLayoutControl" x:ClassModifier="internal"
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:fa="http://schemas.fontawesome.io/icons/"
xmlns:local="clr-namespace:SafeExamBrowser.UserInterface.Mobile.Controls"
mc:Ignorable="d" d:DesignHeight="100" d:DesignWidth="125">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="../../Templates/Buttons.xaml" />
<ResourceDictionary Source="../../Templates/Colors.xaml" />
<ResourceDictionary Source="../../Templates/ScrollViewers.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid x:Name="Grid" Background="{StaticResource ActionCenterDarkBrush}" Height="82" Margin="2">
<Popup x:Name="Popup" IsOpen="False" Placement="Top" PlacementTarget="{Binding ElementName=Button}" KeyUp="Popup_KeyUp">
<Border Background="Gray">
<ScrollViewer MaxHeight="250" VerticalScrollBarVisibility="Auto" Template="{StaticResource SmallBarScrollViewer}">
<StackPanel x:Name="LayoutsStackPanel" />
</ScrollViewer>
</Border>
</Popup>
<Button x:Name="Button" Padding="2" Template="{StaticResource ActionCenterButton}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="2*" />
<RowDefinition Height="3*" />
</Grid.RowDefinitions>
<fa:ImageAwesome Grid.Row="0" Foreground="Black" Icon="KeyboardOutline" Margin="2" VerticalAlignment="Center" />
<TextBlock Grid.Row="1" x:Name="Text" FontSize="15" Foreground="White" TextAlignment="Center" TextTrimming="CharacterEllipsis" TextWrapping="Wrap" VerticalAlignment="Bottom" />
</Grid>
</Button>
</Grid>
</UserControl>

View File

@@ -0,0 +1,141 @@
/*
* Copyright (c) 2024 ETH Zürich, IT Services
*
* 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.Threading.Tasks;
using System.Windows.Automation;
using System.Windows.Controls;
using System.Windows.Media;
using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.SystemComponents.Contracts.Keyboard;
using SafeExamBrowser.UserInterface.Contracts.Shell;
namespace SafeExamBrowser.UserInterface.Mobile.Controls.ActionCenter
{
internal partial class KeyboardLayoutControl : UserControl, ISystemControl
{
private readonly IKeyboard keyboard;
private readonly IText text;
internal KeyboardLayoutControl(IKeyboard keyboard, IText text)
{
this.keyboard = keyboard;
this.text = text;
InitializeComponent();
InitializeKeyboardLayoutControl();
}
public void Close()
{
Dispatcher.Invoke(() => Popup.IsOpen = false);
}
private void InitializeKeyboardLayoutControl()
{
var originalBrush = Grid.Background;
InitializeLayouts();
keyboard.LayoutChanged += Keyboard_LayoutChanged;
Button.Click += (o, args) =>
{
Popup.IsOpen = !Popup.IsOpen;
this.Dispatcher.BeginInvoke((System.Action) (() =>
{
LayoutsStackPanel.Children[0].Focus();
}));
};
var lastOpenedBySpacePress = false;
Button.PreviewKeyDown += (o, args) =>
{
// For some reason, the popup immediately closes again if opened by a Space Bar key event - as a mitigation,
// we record the space bar event and leave the popup open for at least 3 seconds.
if (args.Key == System.Windows.Input.Key.Space)
{
lastOpenedBySpacePress = true;
}
};
Button.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() =>
{
if (Popup.IsOpen && lastOpenedBySpacePress)
{
return;
}
Popup.IsOpen = Popup.IsMouseOver;
}));
Popup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() =>
{
if (Popup.IsOpen && lastOpenedBySpacePress)
{
return;
}
Popup.IsOpen = IsMouseOver;
}));
Popup.Opened += (o, args) => Grid.Background = Brushes.Gray;
Popup.Closed += (o, args) =>
{
Grid.Background = originalBrush;
lastOpenedBySpacePress = false;
};
}
private void Keyboard_LayoutChanged(IKeyboardLayout layout)
{
Dispatcher.InvokeAsync(() => SetCurrent(layout));
}
private void InitializeLayouts()
{
foreach (var layout in keyboard.GetLayouts())
{
var button = new KeyboardLayoutButton(layout);
button.LayoutSelected += (o, args) => ActivateLayout(layout);
LayoutsStackPanel.Children.Add(button);
if (layout.IsCurrent)
{
SetCurrent(layout);
}
}
}
private void ActivateLayout(IKeyboardLayout layout)
{
Popup.IsOpen = false;
keyboard.ActivateLayout(layout.Id);
}
private void SetCurrent(IKeyboardLayout layout)
{
var tooltip = text.Get(TextKey.SystemControl_KeyboardLayoutTooltip).Replace("%%LAYOUT%%", layout.CultureName);
foreach (var child in LayoutsStackPanel.Children)
{
if (child is KeyboardLayoutButton layoutButton)
{
layoutButton.IsCurrent = layout.Id == layoutButton.LayoutId;
}
}
Text.Text = layout.CultureName;
Button.ToolTip = tooltip;
AutomationProperties.SetName(Button, tooltip);
}
private void Popup_KeyUp(object sender, System.Windows.Input.KeyEventArgs e)
{
if (e.Key == System.Windows.Input.Key.Enter || e.Key == System.Windows.Input.Key.Escape)
{
Popup.IsOpen = false;
Button.Focus();
}
}
}
}

View File

@@ -0,0 +1,30 @@
<UserControl x:Class="SafeExamBrowser.UserInterface.Mobile.Controls.ActionCenter.NetworkButton" x:ClassModifier="internal"
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"
mc:Ignorable="d" d:DesignHeight="40" d:DesignWidth="250">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="../../Templates/Buttons.xaml" />
<ResourceDictionary Source="../../Templates/Colors.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid>
<Button x:Name="Button" Background="Transparent" Height="60" Padding="10,0" Template="{StaticResource ActionCenterButton}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock x:Name="IsCurrentTextBlock" Grid.Column="0" FontSize="20" HorizontalAlignment="Center" VerticalAlignment="Center" Visibility="Hidden">•</TextBlock>
<TextBlock x:Name="SignalStrengthTextBlock" Grid.Column="1" Foreground="White" HorizontalAlignment="Left" Margin="10,0,5,0" VerticalAlignment="Center" />
<TextBlock x:Name="NetworkNameTextBlock" Grid.Column="2" FontWeight="Bold" HorizontalAlignment="Left" Margin="5,0,10,0" VerticalAlignment="Center" />
</Grid>
</Button>
</Grid>
</UserControl>

View File

@@ -0,0 +1,43 @@
/*
* Copyright (c) 2024 ETH Zürich, IT Services
*
* 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.Windows;
using System.Windows.Controls;
using SafeExamBrowser.SystemComponents.Contracts.Network;
namespace SafeExamBrowser.UserInterface.Mobile.Controls.ActionCenter
{
internal partial class NetworkButton : UserControl
{
private readonly IWirelessNetwork network;
internal event EventHandler NetworkSelected;
internal NetworkButton(IWirelessNetwork network)
{
this.network = network;
InitializeComponent();
InitializeNetworkButton();
}
private void InitializeNetworkButton()
{
Button.Click += (o, args) => NetworkSelected?.Invoke(this, EventArgs.Empty);
IsCurrentTextBlock.Visibility = network.Status == ConnectionStatus.Connected ? Visibility.Visible : Visibility.Hidden;
NetworkNameTextBlock.Text = network.Name;
SignalStrengthTextBlock.Text = $"{network.SignalStrength}%";
}
public void SetFocus()
{
Button.Focus();
}
}
}

View File

@@ -0,0 +1,43 @@
<UserControl x:Class="SafeExamBrowser.UserInterface.Mobile.Controls.ActionCenter.NetworkControl" x:ClassModifier="internal"
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:fa="http://schemas.fontawesome.io/icons/"
xmlns:local="clr-namespace:SafeExamBrowser.UserInterface.Mobile.Controls"
mc:Ignorable="d" d:DesignHeight="100" d:DesignWidth="125">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="../../Templates/Buttons.xaml" />
<ResourceDictionary Source="../../Templates/Colors.xaml" />
<ResourceDictionary Source="../../Templates/ScrollViewers.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid x:Name="Grid" Background="{StaticResource ActionCenterDarkBrush}" Height="82" Margin="2">
<Popup x:Name="Popup" IsOpen="False" Placement="Top" PlacementTarget="{Binding ElementName=Button}">
<Border Background="Gray">
<ScrollViewer MaxHeight="250" VerticalScrollBarVisibility="Auto" Template="{StaticResource SmallBarScrollViewer}">
<StackPanel x:Name="WirelessNetworksStackPanel" />
</ScrollViewer>
</Border>
</Popup>
<Button x:Name="Button" Padding="2" Template="{StaticResource ActionCenterButton}" ToolTipService.ShowOnDisabled="True">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="2*" />
<RowDefinition Height="3*" />
</Grid.RowDefinitions>
<Grid Grid.Row="0" HorizontalAlignment="Center" VerticalAlignment="Center">
<Viewbox x:Name="WirelessIcon" Stretch="Uniform" Width="Auto" Visibility="Collapsed" />
<fa:ImageAwesome x:Name="WiredIcon" Icon="Tv" Margin="0,2,4,4" Visibility="Collapsed" />
<Border Background="White" CornerRadius="6" Height="14" HorizontalAlignment="Right" Margin="-3,0" Panel.ZIndex="10" VerticalAlignment="Bottom">
<fa:ImageAwesome x:Name="NetworkStatusIcon" />
</Border>
</Grid>
<TextBlock Grid.Row="1" x:Name="Text" FontSize="15" Foreground="White" TextAlignment="Center" TextTrimming="CharacterEllipsis" TextWrapping="Wrap" VerticalAlignment="Bottom" />
</Grid>
</Button>
</Grid>
</UserControl>

View File

@@ -0,0 +1,177 @@
/*
* Copyright (c) 2024 ETH Zürich, IT Services
*
* 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.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using FontAwesome.WPF;
using SafeExamBrowser.Core.Contracts.Resources.Icons;
using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.SystemComponents.Contracts.Network;
using SafeExamBrowser.UserInterface.Contracts.Shell;
using SafeExamBrowser.UserInterface.Shared.Utilities;
namespace SafeExamBrowser.UserInterface.Mobile.Controls.ActionCenter
{
internal partial class NetworkControl : UserControl, ISystemControl
{
private readonly INetworkAdapter adapter;
private readonly IText text;
internal NetworkControl(INetworkAdapter adapter, IText text)
{
this.adapter = adapter;
this.text = text;
InitializeComponent();
InitializeWirelessNetworkControl();
}
public void Close()
{
Dispatcher.InvokeAsync(() => Popup.IsOpen = false);
}
private void InitializeWirelessNetworkControl()
{
var originalBrush = Grid.Background;
adapter.Changed += () => Dispatcher.InvokeAsync(Update);
Button.Click += (o, args) => Popup.IsOpen = !Popup.IsOpen;
var lastOpenedBySpacePress = false;
Button.PreviewKeyDown += (o, args) =>
{
// For some reason, the popup immediately closes again if opened by a Space Bar key event - as a mitigation,
// we record the space bar event and leave the popup open for at least 3 seconds.
if (args.Key == System.Windows.Input.Key.Space)
{
lastOpenedBySpacePress = true;
}
};
Button.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() =>
{
if (Popup.IsOpen && lastOpenedBySpacePress)
{
return;
}
Popup.IsOpen = Popup.IsMouseOver;
}));
Popup.Closed += (o, args) =>
{
adapter.StopWirelessNetworkScanning();
Grid.Background = originalBrush;
lastOpenedBySpacePress = false;
};
Popup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() =>
{
if (Popup.IsOpen && lastOpenedBySpacePress)
{
return;
}
Popup.IsOpen = IsMouseOver;
}));
Popup.Opened += (o, args) =>
{
adapter.StartWirelessNetworkScanning();
Grid.Background = Brushes.Gray;
Task.Delay(100).ContinueWith((task) => Dispatcher.Invoke(() =>
{
if (WirelessNetworksStackPanel.Children.Count > 0)
{
(WirelessNetworksStackPanel.Children[0] as NetworkButton)?.SetFocus();
}
}));
};
WirelessIcon.Child = GetWirelessIcon(0);
Update();
}
private void Update()
{
switch (adapter.Type)
{
case ConnectionType.Wired:
Button.IsEnabled = false;
UpdateText(text.Get(TextKey.SystemControl_NetworkWiredConnected));
WiredIcon.Visibility = Visibility.Visible;
WirelessIcon.Visibility = Visibility.Collapsed;
break;
case ConnectionType.Wireless:
Button.IsEnabled = true;
WiredIcon.Visibility = Visibility.Collapsed;
WirelessIcon.Visibility = Visibility.Visible;
break;
default:
Button.IsEnabled = false;
UpdateText(text.Get(TextKey.SystemControl_NetworkNotAvailable));
WiredIcon.Visibility = Visibility.Visible;
WirelessIcon.Visibility = Visibility.Collapsed;
break;
}
switch (adapter.Status)
{
case ConnectionStatus.Connected:
UpdateText(text.Get(TextKey.SystemControl_NetworkWiredConnected));
NetworkStatusIcon.Rotation = 0;
NetworkStatusIcon.Source = ImageAwesome.CreateImageSource(FontAwesomeIcon.Globe, Brushes.Green);
NetworkStatusIcon.Spin = false;
break;
case ConnectionStatus.Connecting:
UpdateText(text.Get(TextKey.SystemControl_NetworkWirelessConnecting));
NetworkStatusIcon.Rotation = 0;
NetworkStatusIcon.Source = ImageAwesome.CreateImageSource(FontAwesomeIcon.Cog, Brushes.DimGray);
NetworkStatusIcon.Spin = true;
NetworkStatusIcon.SpinDuration = 2;
break;
default:
UpdateText(text.Get(TextKey.SystemControl_NetworkDisconnected));
NetworkStatusIcon.Source = ImageAwesome.CreateImageSource(FontAwesomeIcon.Ban, Brushes.DarkOrange);
NetworkStatusIcon.Spin = false;
WirelessIcon.Child = GetWirelessIcon(0);
break;
}
WirelessNetworksStackPanel.Children.Clear();
foreach (var network in adapter.GetWirelessNetworks())
{
var button = new NetworkButton(network);
button.NetworkSelected += (o, args) => adapter.ConnectToWirelessNetwork(network.Name);
if (network.Status == ConnectionStatus.Connected)
{
WirelessIcon.Child = GetWirelessIcon(network.SignalStrength);
UpdateText(text.Get(TextKey.SystemControl_NetworkWirelessConnected).Replace("%%NAME%%", network.Name));
}
WirelessNetworksStackPanel.Children.Add(button);
}
}
private void UpdateText(string text)
{
Button.ToolTip = text;
Text.Text = text;
Button.SetValue(System.Windows.Automation.AutomationProperties.NameProperty, text);
}
private UIElement GetWirelessIcon(int signalStrength)
{
var icon = signalStrength > 66 ? "100" : (signalStrength > 33 ? "66" : (signalStrength > 0 ? "33" : "0"));
var uri = new Uri($"pack://application:,,,/SafeExamBrowser.UserInterface.Mobile;component/Images/WiFi_Light_{icon}.xaml");
var resource = new XamlIconResource { Uri = uri };
return IconResourceLoader.Load(resource);
}
}
}

View File

@@ -0,0 +1,28 @@
<UserControl x:Class="SafeExamBrowser.UserInterface.Mobile.Controls.ActionCenter.NotificationButton" x:ClassModifier="internal"
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"
mc:Ignorable="d" d:DesignHeight="100" d:DesignWidth="125">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="../../Templates/Buttons.xaml" />
<ResourceDictionary Source="../../Templates/Colors.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid Background="{StaticResource ActionCenterDarkBrush}" Height="82" Margin="2">
<Button x:Name="IconButton" Click="IconButton_Click" Padding="2" Template="{StaticResource ActionCenterButton}" ToolTipService.ShowOnDisabled="True">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="2*" />
<RowDefinition Height="3*" />
</Grid.RowDefinitions>
<ContentControl Grid.Row="0" x:Name="Icon" VerticalAlignment="Center" />
<TextBlock Grid.Row="1" x:Name="Text" FontSize="15" Foreground="White" TextAlignment="Center" TextTrimming="CharacterEllipsis" TextWrapping="Wrap" VerticalAlignment="Bottom" />
</Grid>
</Button>
</Grid>
</UserControl>

View File

@@ -0,0 +1,51 @@
/*
* Copyright (c) 2024 ETH Zürich, IT Services
*
* 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.Core.Contracts.Notifications;
using SafeExamBrowser.UserInterface.Contracts.Shell;
using SafeExamBrowser.UserInterface.Shared.Utilities;
namespace SafeExamBrowser.UserInterface.Mobile.Controls.ActionCenter
{
internal partial class NotificationButton : UserControl, INotificationControl
{
private readonly INotification notification;
internal NotificationButton(INotification notification)
{
this.notification = notification;
InitializeComponent();
InitializeNotification();
UpdateNotification();
}
private void IconButton_Click(object sender, RoutedEventArgs e)
{
if (notification.CanActivate)
{
notification.Activate();
}
}
private void InitializeNotification()
{
notification.NotificationChanged += () => Dispatcher.Invoke(UpdateNotification);
}
private void UpdateNotification()
{
Icon.Content = IconResourceLoader.Load(notification.IconResource);
IconButton.IsEnabled = notification.CanActivate;
IconButton.ToolTip = notification.Tooltip;
Text.Text = notification.Tooltip;
}
}
}

View File

@@ -0,0 +1,51 @@
<UserControl x:Class="SafeExamBrowser.UserInterface.Mobile.Controls.ActionCenter.PowerSupplyControl" x:ClassModifier="internal"
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"
mc:Ignorable="d" d:DesignHeight="100" d:DesignWidth="125">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="../../Templates/Buttons.xaml" />
<ResourceDictionary Source="../../Templates/Colors.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid Background="{StaticResource ActionCenterDarkBrush}" Height="82" Margin="2">
<Button x:Name="Button" IsEnabled="False" Padding="2" ToolTipService.ShowOnDisabled="True" Template="{StaticResource ActionCenterButton}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="2*" />
<RowDefinition Height="3*" />
</Grid.RowDefinitions>
<Viewbox Grid.Row="0" Stretch="Uniform" Width="Auto">
<Canvas Height="40" Width="40">
<Viewbox Stretch="Uniform" Width="40" Panel.ZIndex="2">
<Canvas Width="1024.000" Height="1024.000">
<Canvas.LayoutTransform>
<RotateTransform Angle="180" />
</Canvas.LayoutTransform>
<Canvas>
<Path Data=" M 0.000,0.000 L 1024.000,0.000 L 1024.000,1024.000 L 0.000,1024.000 L 0.000,0.000 Z"/>
<Path StrokeThickness="35.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 112.000,300.000 L 951.000,300.000 C 970.330,300.000 986.000,315.670 986.000,335.000 L 986.000,689.000 C 986.000,708.330 970.330,724.000 951.000,724.000 L 112.000,724.000 C 92.670,724.000 77.000,708.330 77.000,689.000 L 77.000,335.000 C 77.000,315.670 92.670,300.000 112.000,300.000 Z"/>
<Path StrokeThickness="1.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Fill="#ff000000" Data=" M 25.000,462.500 L 50.000,462.500 C 52.760,462.500 55.000,464.740 55.000,467.500 L 55.000,557.500 C 55.000,560.260 52.760,562.500 50.000,562.500 L 25.000,562.500 C 22.240,562.500 20.000,560.260 20.000,557.500 L 20.000,467.500 C 20.000,464.740 22.240,462.500 25.000,462.500 Z"/>
</Canvas>
</Canvas>
</Viewbox>
<Rectangle x:Name="BatteryCharge" Canvas.Left="2" Canvas.Top="12" Fill="Green" Height="16" Width="35" Panel.ZIndex="1" />
<Canvas x:Name="PowerPlug" Panel.ZIndex="3" Canvas.Left="4" Canvas.Top="-3">
<Canvas.LayoutTransform>
<ScaleTransform ScaleX="2" ScaleY="2" />
</Canvas.LayoutTransform>
<Path Stroke="Black" StrokeStartLineCap="Round" Fill="Black" Data="M2.5,17.5 V10 Q5,10 5,6 H4 V4 H4 V6 H1 V4 H1 V6 H0 Q0,10 2.5,10" />
</Canvas>
<TextBlock x:Name="Warning" FontSize="36" FontWeight="ExtraBold" Foreground="Red" Canvas.Left="13" Canvas.Top="-7" Panel.ZIndex="3">!</TextBlock>
</Canvas>
</Viewbox>
<TextBlock Grid.Row="1" x:Name="Text" FontSize="15" Foreground="White" TextAlignment="Center" TextTrimming="CharacterEllipsis" TextWrapping="WrapWithOverflow" VerticalAlignment="Bottom" />
</Grid>
</Button>
</Grid>
</UserControl>

View File

@@ -0,0 +1,128 @@
/*
* Copyright (c) 2024 ETH Zürich, IT Services
*
* 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.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.SystemComponents.Contracts.PowerSupply;
using SafeExamBrowser.UserInterface.Contracts.Shell;
namespace SafeExamBrowser.UserInterface.Mobile.Controls.ActionCenter
{
internal partial class PowerSupplyControl : UserControl, ISystemControl
{
private Brush initialBrush;
private bool infoShown, warningShown;
private double maxWidth;
private IPowerSupply powerSupply;
private IText text;
internal PowerSupplyControl(IPowerSupply powerSupply, IText text)
{
this.powerSupply = powerSupply;
this.text = text;
InitializeComponent();
InitializePowerSupplyControl();
}
public void Close()
{
}
public void SetInformation(string text)
{
Dispatcher.InvokeAsync(() => Text.Text = text);
}
private void InitializePowerSupplyControl()
{
initialBrush = BatteryCharge.Fill;
maxWidth = BatteryCharge.Width;
powerSupply.StatusChanged += PowerSupply_StatusChanged;
UpdateStatus(powerSupply.GetStatus());
}
private void PowerSupply_StatusChanged(IPowerSupplyStatus status)
{
Dispatcher.InvokeAsync(() => UpdateStatus(status));
}
private void UpdateStatus(IPowerSupplyStatus status)
{
var percentage = Math.Round(status.BatteryCharge * 100);
var tooltip = string.Empty;
RenderCharge(status.BatteryCharge, status.BatteryChargeStatus);
if (status.IsOnline)
{
infoShown = false;
warningShown = false;
tooltip = text.Get(percentage == 100 ? TextKey.SystemControl_BatteryCharged : TextKey.SystemControl_BatteryCharging);
tooltip = tooltip.Replace("%%CHARGE%%", percentage.ToString());
}
else
{
tooltip = text.Get(TextKey.SystemControl_BatteryRemainingCharge);
tooltip = tooltip.Replace("%%CHARGE%%", percentage.ToString());
tooltip = tooltip.Replace("%%HOURS%%", status.BatteryTimeRemaining.Hours.ToString());
tooltip = tooltip.Replace("%%MINUTES%%", status.BatteryTimeRemaining.Minutes.ToString());
HandleBatteryStatus(status.BatteryChargeStatus);
}
if (!infoShown && !warningShown)
{
Button.ToolTip = tooltip;
}
PowerPlug.Visibility = status.IsOnline ? Visibility.Visible : Visibility.Collapsed;
Text.Text = tooltip;
Warning.Visibility = status.BatteryChargeStatus == BatteryChargeStatus.Critical ? Visibility.Visible : Visibility.Collapsed;
this.SetValue(System.Windows.Automation.AutomationProperties.NameProperty, tooltip);
}
private void RenderCharge(double charge, BatteryChargeStatus status)
{
var width = maxWidth * charge;
BatteryCharge.Width = width > maxWidth ? maxWidth : (width < 0 ? 0 : width);
switch (status)
{
case BatteryChargeStatus.Critical:
BatteryCharge.Fill = Brushes.Red;
break;
case BatteryChargeStatus.Low:
BatteryCharge.Fill = Brushes.Orange;
break;
default:
BatteryCharge.Fill = initialBrush;
break;
}
}
private void HandleBatteryStatus(BatteryChargeStatus chargeStatus)
{
if (chargeStatus == BatteryChargeStatus.Low && !infoShown)
{
Button.ToolTip = text.Get(TextKey.SystemControl_BatteryChargeLowInfo);
infoShown = true;
}
if (chargeStatus == BatteryChargeStatus.Critical && !warningShown)
{
Button.ToolTip = text.Get(TextKey.SystemControl_BatteryChargeCriticalWarning);
warningShown = true;
}
}
}
}

View File

@@ -0,0 +1,28 @@
<UserControl x:Class="SafeExamBrowser.UserInterface.Mobile.Controls.ActionCenter.QuitButton" x:ClassModifier="internal"
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"
mc:Ignorable="d" d:DesignHeight="100" d:DesignWidth="125">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="../../Templates/Buttons.xaml" />
<ResourceDictionary Source="../../Templates/Colors.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid Background="{StaticResource ActionCenterDarkBrush}" Height="82" Margin="2">
<Button x:Name="Button" Padding="2" Template="{StaticResource ActionCenterButton}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="2*" />
<RowDefinition Height="3*" />
</Grid.RowDefinitions>
<ContentControl Grid.Row="0" x:Name="Icon" Foreground="Black" VerticalAlignment="Center" />
<TextBlock Grid.Row="1" x:Name="Text" FontSize="15" Foreground="White" TextAlignment="Center" TextTrimming="CharacterEllipsis" TextWrapping="Wrap" VerticalAlignment="Bottom" />
</Grid>
</Button>
</Grid>
</UserControl>

View File

@@ -0,0 +1,37 @@
/*
* Copyright (c) 2024 ETH Zürich, IT Services
*
* 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.ComponentModel;
using System.Windows.Controls;
using SafeExamBrowser.Core.Contracts.Resources.Icons;
using SafeExamBrowser.UserInterface.Contracts.Shell.Events;
using SafeExamBrowser.UserInterface.Shared.Utilities;
namespace SafeExamBrowser.UserInterface.Mobile.Controls.ActionCenter
{
internal partial class QuitButton : UserControl
{
internal event QuitButtonClickedEventHandler Clicked;
public QuitButton()
{
InitializeComponent();
InitializeControl();
}
private void InitializeControl()
{
var uri = new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Desktop;component/Images/ShutDown.xaml");
var resource = new XamlIconResource { Uri = uri };
Icon.Content = IconResourceLoader.Load(resource);
Button.Click += (o, args) => Clicked?.Invoke(new CancelEventArgs());
}
}
}

View File

@@ -0,0 +1,42 @@
<UserControl x:Class="SafeExamBrowser.UserInterface.Mobile.Controls.ActionCenter.RaiseHandControl"
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"
mc:Ignorable="d" d:DesignHeight="100" d:DesignWidth="125">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="../../Templates/Buttons.xaml" />
<ResourceDictionary Source="../../Templates/Colors.xaml" />
<ResourceDictionary Source="../../Templates/ScrollViewers.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid Name="Grid" Background="{StaticResource ActionCenterDarkBrush}" Height="82" Margin="2">
<Popup x:Name="Popup" IsOpen="False" Placement="Custom" PlacementTarget="{Binding ElementName=Button}">
<Border Background="Gray" BorderThickness="0" >
<StackPanel>
<TextBox Name="Message" AcceptsReturn="True" Height="150" IsReadOnly="False" Margin="5,5,5,0" Width="350" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" />
<Grid>
<Button Name="HandButton" Background="Transparent" Height="30" Margin="5" Padding="5" Template="{StaticResource TaskbarButton}" Width="150">
<Viewbox Stretch="Uniform">
<TextBlock x:Name="HandButtonText" FontWeight="Bold" TextAlignment="Center" />
</Viewbox>
</Button>
</Grid>
</StackPanel>
</Border>
</Popup>
<Button x:Name="NotificationButton" Padding="2" Template="{StaticResource ActionCenterButton}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="2*" />
<RowDefinition Height="3*" />
</Grid.RowDefinitions>
<ContentControl x:Name="Icon" />
<TextBlock Grid.Row="1" x:Name="Text" FontSize="11" Foreground="White" TextAlignment="Center" TextTrimming="CharacterEllipsis" TextWrapping="Wrap" VerticalAlignment="Bottom" />
</Grid>
</Button>
</Grid>
</UserControl>

View File

@@ -0,0 +1,159 @@
/*
* Copyright (c) 2024 ETH Zürich, IT Services
*
* 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.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Media;
using SafeExamBrowser.Core.Contracts.Resources.Icons;
using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.Proctoring.Contracts;
using SafeExamBrowser.Settings.Proctoring;
using SafeExamBrowser.UserInterface.Contracts.Shell;
using SafeExamBrowser.UserInterface.Shared.Utilities;
namespace SafeExamBrowser.UserInterface.Mobile.Controls.ActionCenter
{
public partial class RaiseHandControl : UserControl, INotificationControl
{
private readonly IProctoringController controller;
private readonly ProctoringSettings settings;
private readonly IText text;
private IconResource LoweredIcon;
private IconResource RaisedIcon;
public RaiseHandControl(IProctoringController controller, ProctoringSettings settings, IText text)
{
this.controller = controller;
this.settings = settings;
this.text = text;
InitializeComponent();
InitializeRaiseHandControl();
}
private void InitializeRaiseHandControl()
{
var originalBrush = Grid.Background;
controller.HandLowered += () => Dispatcher.Invoke(ShowLowered);
controller.HandRaised += () => Dispatcher.Invoke(ShowRaised);
HandButton.Click += RaiseHandButton_Click;
HandButtonText.Text = text.Get(TextKey.Notification_ProctoringRaiseHand);
LoweredIcon = new XamlIconResource { Uri = new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Desktop;component/Images/Hand_Lowered.xaml") };
RaisedIcon = new XamlIconResource { Uri = new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Desktop;component/Images/Hand_Raised.xaml") };
Icon.Content = IconResourceLoader.Load(LoweredIcon);
var lastOpenedBySpacePress = false;
NotificationButton.PreviewKeyDown += (o, args) =>
{
if (args.Key == System.Windows.Input.Key.Space) // for some reason, the popup immediately closes again if opened by a Space Bar key event - as a mitigation, we record the space bar event and leave the popup open for at least 3 seconds
{
lastOpenedBySpacePress = true;
}
};
NotificationButton.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() =>
{
if (Popup.IsOpen && lastOpenedBySpacePress)
{
return;
}
Popup.IsOpen = Popup.IsMouseOver;
}));
NotificationButton.PreviewMouseLeftButtonUp += NotificationButton_PreviewMouseLeftButtonUp;
NotificationButton.PreviewMouseRightButtonUp += NotificationButton_PreviewMouseRightButtonUp;
NotificationButton.ToolTip = text.Get(TextKey.Notification_ProctoringHandLowered);
Popup.CustomPopupPlacementCallback = new CustomPopupPlacementCallback(Popup_PlacementCallback);
Popup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() =>
{
if (Popup.IsOpen && lastOpenedBySpacePress)
{
return;
}
Popup.IsOpen = IsMouseOver;
}));
Popup.Opened += (o, args) => Grid.Background = Brushes.Gray;
Popup.Closed += (o, args) =>
{
Grid.Background = originalBrush;
lastOpenedBySpacePress = false;
};
Text.Text = text.Get(TextKey.Notification_ProctoringHandLowered);
}
private void NotificationButton_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if (settings.ForceRaiseHandMessage || Popup.IsOpen)
{
Popup.IsOpen = !Popup.IsOpen;
}
else
{
ToggleHand();
}
}
private void NotificationButton_PreviewMouseRightButtonUp(object sender, MouseButtonEventArgs e)
{
Popup.IsOpen = !Popup.IsOpen;
}
private CustomPopupPlacement[] Popup_PlacementCallback(Size popupSize, Size targetSize, Point offset)
{
return new[]
{
new CustomPopupPlacement(new Point(targetSize.Width / 2 - popupSize.Width / 2, -popupSize.Height), PopupPrimaryAxis.None)
};
}
private void RaiseHandButton_Click(object sender, RoutedEventArgs e)
{
ToggleHand();
}
private void ToggleHand()
{
if (controller.IsHandRaised)
{
controller.LowerHand();
}
else
{
controller.RaiseHand(Message.Text);
Message.Clear();
}
}
private void ShowLowered()
{
HandButtonText.Text = text.Get(TextKey.Notification_ProctoringRaiseHand);
Icon.Content = IconResourceLoader.Load(LoweredIcon);
Message.IsEnabled = true;
NotificationButton.ToolTip = text.Get(TextKey.Notification_ProctoringHandLowered);
Popup.IsOpen = false;
Text.Text = text.Get(TextKey.Notification_ProctoringHandLowered);
}
private void ShowRaised()
{
HandButtonText.Text = text.Get(TextKey.Notification_ProctoringLowerHand);
Icon.Content = IconResourceLoader.Load(RaisedIcon);
Message.IsEnabled = false;
NotificationButton.ToolTip = text.Get(TextKey.Notification_ProctoringHandRaised);
Text.Text = text.Get(TextKey.Notification_ProctoringHandRaised);
}
}
}

View File

@@ -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>

View File

@@ -0,0 +1,56 @@
/*
* Copyright (c) 2024 ETH Zürich, IT Services
*
* 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);
}
}
}
}

View File

@@ -0,0 +1,31 @@
<UserControl x:Class="SafeExamBrowser.UserInterface.Mobile.Controls.Taskbar.ApplicationControl" x:ClassModifier="internal"
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"
xmlns:s="clr-namespace:System;assembly=mscorlib"
mc:Ignorable="d" d:DesignHeight="40" d:DesignWidth="50">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="../../Templates/Buttons.xaml" />
<ResourceDictionary Source="../../Templates/Colors.xaml" />
<ResourceDictionary Source="../../Templates/ScrollViewers.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid>
<Popup x:Name="WindowPopup" IsOpen="False" Placement="Custom" PlacementTarget="{Binding ElementName=Button}">
<Border Background="LightGray" BorderBrush="Gray" BorderThickness="1,1,1,0">
<ScrollViewer MaxHeight="400" VerticalScrollBarVisibility="Auto" Template="{StaticResource SmallBarScrollViewer}">
<StackPanel x:Name="WindowStackPanel" />
</ScrollViewer>
</Border>
</Popup>
<Button x:Name="Button" Background="{StaticResource BackgroundBrush}" Padding="4" Template="{StaticResource TaskbarButton}" Width="60" />
<Grid>
<Rectangle x:Name="ActiveBar" Cursor="Hand" Height="2.5" Width="40" VerticalAlignment="Bottom" Fill="DodgerBlue" Visibility="Collapsed" />
</Grid>
</Grid>
</UserControl>

View File

@@ -0,0 +1,118 @@
/*
* Copyright (c) 2024 ETH Zürich, IT Services
*
* 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.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Automation;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Media;
using System.Windows.Threading;
using SafeExamBrowser.Applications.Contracts;
using SafeExamBrowser.UserInterface.Contracts.Shell;
using SafeExamBrowser.UserInterface.Shared.Utilities;
namespace SafeExamBrowser.UserInterface.Mobile.Controls.Taskbar
{
internal partial class ApplicationControl : UserControl, IApplicationControl
{
private readonly IApplication<IApplicationWindow> application;
private IApplicationWindow single;
internal ApplicationControl(IApplication<IApplicationWindow> application)
{
this.application = application;
InitializeComponent();
InitializeApplicationControl();
}
private void InitializeApplicationControl()
{
var originalBrush = Button.Background;
application.WindowsChanged += Application_WindowsChanged;
ActiveBar.MouseLeave += (o, args) => WindowPopup.IsOpen &= WindowPopup.IsMouseOver || Button.IsMouseOver;
Button.Click += Button_Click;
Button.Content = IconResourceLoader.Load(application.Icon);
Button.MouseEnter += (o, args) => WindowPopup.IsOpen = WindowStackPanel.Children.Count > 0;
Button.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => WindowPopup.IsOpen = WindowPopup.IsMouseOver || ActiveBar.IsMouseOver));
Button.ToolTip = application.Tooltip;
WindowPopup.CustomPopupPlacementCallback = new CustomPopupPlacementCallback(WindowPopup_PlacementCallback);
WindowPopup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => WindowPopup.IsOpen = IsMouseOver));
if (application.Tooltip != default)
{
AutomationProperties.SetName(Button, application.Tooltip);
}
WindowPopup.Opened += (o, args) =>
{
ActiveBar.Width = double.NaN;
Background = Brushes.LightGray;
Button.Background = Brushes.LightGray;
};
WindowPopup.Closed += (o, args) =>
{
ActiveBar.Width = 40;
Background = originalBrush;
Button.Background = originalBrush;
};
}
private void Application_WindowsChanged()
{
Dispatcher.InvokeAsync(Update);
}
private void Button_Click(object sender, RoutedEventArgs e)
{
if (WindowStackPanel.Children.Count == 0)
{
application.Start();
}
else if (WindowStackPanel.Children.Count == 1)
{
single?.Activate();
}
}
private CustomPopupPlacement[] WindowPopup_PlacementCallback(Size popupSize, Size targetSize, Point offset)
{
return new[]
{
new CustomPopupPlacement(new Point(targetSize.Width / 2 - popupSize.Width / 2, -popupSize.Height), PopupPrimaryAxis.None)
};
}
private void Update()
{
var windows = application.GetWindows();
ActiveBar.Visibility = windows.Any() ? Visibility.Visible : Visibility.Collapsed;
WindowStackPanel.Children.Clear();
foreach (var window in windows)
{
WindowStackPanel.Children.Add(new ApplicationWindowButton(window));
}
if (WindowStackPanel.Children.Count == 1)
{
single = windows.First();
}
else
{
single = default;
}
}
}
}

View File

@@ -0,0 +1,24 @@
<UserControl x:Class="SafeExamBrowser.UserInterface.Mobile.Controls.Taskbar.ApplicationWindowButton" x:ClassModifier="internal"
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"
mc:Ignorable="d" d:DesignWidth="250">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="../../Templates/Buttons.xaml" />
<ResourceDictionary Source="../../Templates/Colors.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid>
<Button x:Name="Button" Background="Transparent" Height="60" Padding="10" Template="{StaticResource TaskbarButton}">
<StackPanel Orientation="Horizontal">
<ContentControl x:Name="Icon" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="0,0,10,0" Width="20" />
<TextBlock x:Name="Text" HorizontalAlignment="Left" VerticalAlignment="Center" Padding="5" MaxWidth="350" TextTrimming="CharacterEllipsis" />
</StackPanel>
</Button>
</Grid>
</UserControl>

View File

@@ -0,0 +1,58 @@
/*
* Copyright (c) 2024 ETH Zürich, IT Services
*
* 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.Resources.Icons;
using SafeExamBrowser.UserInterface.Shared.Utilities;
namespace SafeExamBrowser.UserInterface.Mobile.Controls.Taskbar
{
internal partial class ApplicationWindowButton : UserControl
{
private IApplicationWindow window;
internal ApplicationWindowButton(IApplicationWindow window)
{
this.window = window;
InitializeComponent();
InitializeApplicationInstanceButton();
}
private void InitializeApplicationInstanceButton()
{
Button.Click += Button_Click;
Button.ToolTip = window.Title;
Icon.Content = IconResourceLoader.Load(window.Icon);
window.IconChanged += Instance_IconChanged;
window.TitleChanged += Window_TitleChanged;
Text.Text = window.Title;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
window.Activate();
}
private void Instance_IconChanged(IconResource icon)
{
Dispatcher.InvokeAsync(() => Icon.Content = IconResourceLoader.Load(icon));
}
private void Window_TitleChanged(string title)
{
Dispatcher.InvokeAsync(() =>
{
Text.Text = title;
Button.ToolTip = title;
});
}
}
}

View File

@@ -0,0 +1,43 @@
<UserControl x:Class="SafeExamBrowser.UserInterface.Mobile.Controls.Taskbar.AudioControl" x:ClassModifier="internal"
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:fa="http://schemas.fontawesome.io/icons/"
xmlns:local="clr-namespace:SafeExamBrowser.UserInterface.Mobile.Controls"
mc:Ignorable="d" d:DesignHeight="40" d:DesignWidth="40">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="../../Templates/Buttons.xaml" />
<ResourceDictionary Source="../../Templates/Colors.xaml" />
<ResourceDictionary Source="../../Templates/ScrollViewers.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid>
<Popup x:Name="Popup" IsOpen="False" Placement="Custom" PlacementTarget="{Binding ElementName=Button}" KeyDown="Popup_KeyDown">
<Border Background="LightGray" BorderBrush="Gray" BorderThickness="1,1,1,0">
<StackPanel Orientation="Vertical">
<TextBlock x:Name="AudioDeviceName" Margin="5" TextAlignment="Center" />
<StackPanel Orientation="Horizontal" Height="60" Margin="5,0">
<Button x:Name="MuteButton" Background="Transparent" Padding="5" Template="{StaticResource TaskbarButton}" Width="60">
<ContentControl x:Name="PopupIcon" Focusable="False" />
</Button>
<Slider x:Name="Volume" Grid.Column="1" Orientation="Horizontal" TickFrequency="1" Maximum="100" IsSnapToTickEnabled="True" KeyDown="Volume_KeyDown"
IsMoveToPointEnabled="True" VerticalAlignment="Center" Width="125" Thumb.DragStarted="Volume_DragStarted" Thumb.DragCompleted="Volume_DragCompleted">
<Slider.LayoutTransform>
<ScaleTransform ScaleX="2" ScaleY="2" CenterX="0" CenterY="0"/>
</Slider.LayoutTransform>
</Slider>
<TextBlock Grid.Column="2" FontWeight="DemiBold" FontSize="20" Text="{Binding ElementName=Volume, Path=Value}"
TextAlignment="Center" VerticalAlignment="Center" Width="60" />
</StackPanel>
</StackPanel>
</Border>
</Popup>
<Button x:Name="Button" Background="Transparent" Padding="5" Template="{StaticResource TaskbarButton}" ToolTipService.ShowOnDisabled="True" Width="60">
<ContentControl x:Name="ButtonIcon" Focusable="False" />
</Button>
</Grid>
</UserControl>

View File

@@ -0,0 +1,226 @@
/*
* Copyright (c) 2024 ETH Zürich, IT Services
*
* 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.Threading.Tasks;
using System.Windows;
using System.Windows.Automation;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Media;
using SafeExamBrowser.Core.Contracts.Resources.Icons;
using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.SystemComponents.Contracts.Audio;
using SafeExamBrowser.UserInterface.Contracts.Shell;
using SafeExamBrowser.UserInterface.Shared.Utilities;
namespace SafeExamBrowser.UserInterface.Mobile.Controls.Taskbar
{
internal partial class AudioControl : UserControl, ISystemControl
{
private readonly IAudio audio;
private readonly IText text;
private bool muted;
private IconResource MutedIcon;
private IconResource NoDeviceIcon;
internal AudioControl(IAudio audio, IText text)
{
this.audio = audio;
this.text = text;
InitializeComponent();
InitializeAudioControl();
}
public void Close()
{
Popup.IsOpen = false;
}
private void InitializeAudioControl()
{
var originalBrush = Button.Background;
audio.VolumeChanged += Audio_VolumeChanged;
Button.Click += (o, args) => Popup.IsOpen = !Popup.IsOpen;
var lastOpenedBySpacePress = false;
Button.PreviewKeyDown += (o, args) =>
{
// For some reason, the popup immediately closes again if opened by a Space Bar key event - as a mitigation,
// we record the space bar event and leave the popup open for at least 3 seconds.
if (args.Key == System.Windows.Input.Key.Space)
{
lastOpenedBySpacePress = true;
}
};
Button.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() =>
{
if (Popup.IsOpen && lastOpenedBySpacePress)
{
return;
}
Popup.IsOpen = Popup.IsMouseOver;
}));
MuteButton.Click += MuteButton_Click;
MutedIcon = new XamlIconResource { Uri = new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Mobile;component/Images/Audio_Muted.xaml") };
NoDeviceIcon = new XamlIconResource { Uri = new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Mobile;component/Images/Audio_NoDevice.xaml") };
Popup.CustomPopupPlacementCallback = new CustomPopupPlacementCallback(Popup_PlacementCallback);
Popup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() =>
{
if (Popup.IsOpen && lastOpenedBySpacePress)
{
return;
}
Popup.IsOpen = IsMouseOver;
}));
Volume.ValueChanged += Volume_ValueChanged;
Popup.Opened += (o, args) =>
{
Background = Brushes.LightGray;
Button.Background = Brushes.LightGray;
Volume.Focus();
};
Popup.Closed += (o, args) =>
{
Background = originalBrush;
Button.Background = originalBrush;
lastOpenedBySpacePress = false;
};
if (audio.HasOutputDevice)
{
AudioDeviceName.Text = audio.DeviceFullName;
Button.IsEnabled = true;
UpdateVolume(audio.OutputVolume, audio.OutputMuted);
}
else
{
AudioDeviceName.Text = text.Get(TextKey.SystemControl_AudioDeviceNotFound);
Button.IsEnabled = false;
Button.ToolTip = text.Get(TextKey.SystemControl_AudioDeviceNotFound);
ButtonIcon.Content = IconResourceLoader.Load(NoDeviceIcon);
}
}
private void Audio_VolumeChanged(double volume, bool muted)
{
Dispatcher.InvokeAsync(() => UpdateVolume(volume, muted));
}
private void MuteButton_Click(object sender, RoutedEventArgs e)
{
if (muted)
{
audio.Unmute();
}
else
{
audio.Mute();
}
}
private CustomPopupPlacement[] Popup_PlacementCallback(Size popupSize, Size targetSize, Point offset)
{
return new[]
{
new CustomPopupPlacement(new Point(targetSize.Width / 2 - popupSize.Width / 2, -popupSize.Height), PopupPrimaryAxis.None)
};
}
private void Volume_DragStarted(object sender, DragStartedEventArgs e)
{
Volume.ValueChanged -= Volume_ValueChanged;
}
private void Volume_DragCompleted(object sender, DragCompletedEventArgs e)
{
audio.SetVolume(Volume.Value / 100);
Volume.ValueChanged += Volume_ValueChanged;
}
private void Volume_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
audio.SetVolume(Volume.Value / 100);
}
private void UpdateVolume(double volume, bool muted)
{
var info = BuildInfoText(volume, muted);
this.muted = muted;
Button.ToolTip = info;
Volume.ValueChanged -= Volume_ValueChanged;
Volume.Value = Math.Round(volume * 100);
Volume.ValueChanged += Volume_ValueChanged;
AutomationProperties.SetName(Button, info);
if (muted)
{
var tooltip = text.Get(TextKey.SystemControl_AudioDeviceUnmuteTooltip);
MuteButton.ToolTip = tooltip;
PopupIcon.Content = IconResourceLoader.Load(MutedIcon);
ButtonIcon.Content = IconResourceLoader.Load(MutedIcon);
AutomationProperties.SetName(MuteButton, tooltip);
}
else
{
var tooltip = text.Get(TextKey.SystemControl_AudioDeviceMuteTooltip);
MuteButton.ToolTip = tooltip;
PopupIcon.Content = LoadIcon(volume);
ButtonIcon.Content = LoadIcon(volume);
AutomationProperties.SetName(MuteButton, tooltip);
}
}
private string BuildInfoText(double volume, bool muted)
{
var info = text.Get(muted ? TextKey.SystemControl_AudioDeviceInfoMuted : TextKey.SystemControl_AudioDeviceInfo);
info = info.Replace("%%NAME%%", audio.DeviceShortName);
info = info.Replace("%%VOLUME%%", Convert.ToString(Math.Round(volume * 100)));
return info;
}
private UIElement LoadIcon(double volume)
{
var icon = volume > 0.66 ? "100" : (volume > 0.33 ? "66" : "33");
var uri = new Uri($"pack://application:,,,/SafeExamBrowser.UserInterface.Mobile;component/Images/Audio_{icon}.xaml");
var resource = new XamlIconResource { Uri = uri };
return IconResourceLoader.Load(resource);
}
private void Popup_KeyDown(object sender, System.Windows.Input.KeyEventArgs e)
{
if (e.Key == System.Windows.Input.Key.Escape)
{
Popup.IsOpen = false;
Button.Focus();
}
}
private void Volume_KeyDown(object sender, System.Windows.Input.KeyEventArgs e)
{
if (e.Key == System.Windows.Input.Key.Enter)
{
Popup.IsOpen = false;
Button.Focus();
}
}
}
}

View File

@@ -0,0 +1,17 @@
<UserControl x:Class="SafeExamBrowser.UserInterface.Mobile.Controls.Taskbar.Clock" x:ClassModifier="internal"
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"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid Background="Transparent" Margin="5,0" ToolTip="{Binding Path=ToolTip}">
<Grid.RowDefinitions>
<RowDefinition Height="1*" />
<RowDefinition Height="1*" />
</Grid.RowDefinitions>
<TextBlock x:Name="TimeTextBlock" Grid.Row="0" Text="{Binding Path=Time}" FontWeight="Bold" HorizontalAlignment="Center" VerticalAlignment="Bottom" Focusable="True" AutomationProperties.Name="{Binding Path=Time}" />
<TextBlock x:Name="DateTextBlock" Grid.Row="1" Text="{Binding Path=Date}" HorizontalAlignment="Center" VerticalAlignment="Top" Focusable="True" AutomationProperties.Name="{Binding Path=Date}" />
</Grid>
</UserControl>

View File

@@ -0,0 +1,32 @@
/*
* Copyright (c) 2024 ETH Zürich, IT Services
*
* 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.Controls;
using SafeExamBrowser.UserInterface.Mobile.ViewModels;
namespace SafeExamBrowser.UserInterface.Mobile.Controls.Taskbar
{
internal partial class Clock : UserControl
{
private DateTimeViewModel model;
public Clock()
{
InitializeComponent();
InitializeControl();
}
private void InitializeControl()
{
model = new DateTimeViewModel(false);
DataContext = model;
TimeTextBlock.DataContext = model;
DateTextBlock.DataContext = model;
}
}
}

View File

@@ -0,0 +1,35 @@
<UserControl x:Class="SafeExamBrowser.UserInterface.Mobile.Controls.Taskbar.KeyboardLayoutButton" x:ClassModifier="internal"
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:fa="http://schemas.fontawesome.io/icons/"
xmlns:local="clr-namespace:SafeExamBrowser.UserInterface.Mobile.Controls"
mc:Ignorable="d" d:DesignHeight="40" d:DesignWidth="250">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="../../Templates/Buttons.xaml" />
<ResourceDictionary Source="../../Templates/Colors.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Button x:Name="Button" Background="Transparent" Height="60" Padding="10,0" Template="{StaticResource TaskbarButton}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock x:Name="IsCurrentTextBlock" Grid.Column="0" FontSize="20" HorizontalAlignment="Center" VerticalAlignment="Center" Visibility="Hidden">•</TextBlock>
<TextBlock x:Name="CultureCodeTextBlock" Grid.Column="1" FontWeight="Bold" HorizontalAlignment="Left" Margin="10,0,5,0" VerticalAlignment="Center" />
<StackPanel Grid.Column="2" VerticalAlignment="Center">
<TextBlock x:Name="CultureNameTextBlock" HorizontalAlignment="Left" Margin="5,0,10,0" TextDecorations="Underline" VerticalAlignment="Center" />
<StackPanel Orientation="Horizontal">
<fa:ImageAwesome Foreground="Gray" Height="10" Icon="KeyboardOutline" Margin="5,0" />
<TextBlock x:Name="LayoutNameTextBlock" Foreground="Gray" HorizontalAlignment="Left" Margin="0,0,10,0" VerticalAlignment="Center" />
</StackPanel>
</StackPanel>
</Grid>
</Button>
</UserControl>

View File

@@ -0,0 +1,51 @@
/*
* Copyright (c) 2024 ETH Zürich, IT Services
*
* 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.Windows;
using System.Windows.Automation;
using System.Windows.Controls;
using SafeExamBrowser.SystemComponents.Contracts.Keyboard;
namespace SafeExamBrowser.UserInterface.Mobile.Controls.Taskbar
{
internal partial class KeyboardLayoutButton : UserControl
{
private readonly IKeyboardLayout layout;
internal bool IsCurrent
{
set { IsCurrentTextBlock.Visibility = value ? Visibility.Visible : Visibility.Hidden; }
}
internal Guid LayoutId
{
get { return layout.Id; }
}
internal event EventHandler LayoutSelected;
internal KeyboardLayoutButton(IKeyboardLayout layout)
{
this.layout = layout;
InitializeComponent();
InitializeLayoutButton();
}
private void InitializeLayoutButton()
{
Button.Click += (o, args) => LayoutSelected?.Invoke(this, EventArgs.Empty);
CultureCodeTextBlock.Text = layout.CultureCode;
CultureNameTextBlock.Text = layout.CultureName;
LayoutNameTextBlock.Text = layout.LayoutName;
AutomationProperties.SetName(Button, layout.CultureName);
}
}
}

View File

@@ -0,0 +1,32 @@
<UserControl x:Class="SafeExamBrowser.UserInterface.Mobile.Controls.Taskbar.KeyboardLayoutControl" x:ClassModifier="internal"
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:s="clr-namespace:System;assembly=mscorlib"
xmlns:local="clr-namespace:SafeExamBrowser.UserInterface.Mobile.Controls"
mc:Ignorable="d" d:DesignHeight="40" d:DesignWidth="40">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="../../Templates/Buttons.xaml" />
<ResourceDictionary Source="../../Templates/Colors.xaml" />
<ResourceDictionary Source="../../Templates/ScrollViewers.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid>
<Popup x:Name="Popup" IsOpen="False" Placement="Custom" PlacementTarget="{Binding ElementName=Button}" KeyUp="Popup_KeyUp">
<Border Background="LightGray" BorderBrush="Gray" BorderThickness="1,1,1,0">
<ScrollViewer x:Name="LayoutsScrollViewer" MaxHeight="250" VerticalScrollBarVisibility="Auto" Template="{StaticResource SmallBarScrollViewer}">
<StackPanel x:Name="LayoutsStackPanel" />
</ScrollViewer>
</Border>
</Popup>
<Button x:Name="Button" Background="Transparent" Template="{StaticResource TaskbarButton}" Padding="5" Width="60">
<Viewbox Stretch="Uniform">
<TextBlock x:Name="LayoutCultureCode" FontWeight="Bold" Margin="2" TextAlignment="Center" VerticalAlignment="Center" />
</Viewbox>
</Button>
</Grid>
</UserControl>

View File

@@ -0,0 +1,162 @@
/*
* Copyright (c) 2024 ETH Zürich, IT Services
*
* 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.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Automation;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Media;
using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.SystemComponents.Contracts.Keyboard;
using SafeExamBrowser.UserInterface.Contracts.Shell;
namespace SafeExamBrowser.UserInterface.Mobile.Controls.Taskbar
{
internal partial class KeyboardLayoutControl : UserControl, ISystemControl
{
private readonly IKeyboard keyboard;
private readonly IText text;
internal KeyboardLayoutControl(IKeyboard keyboard, IText text)
{
this.keyboard = keyboard;
this.text = text;
InitializeComponent();
InitializeKeyboardLayoutControl();
}
public void Close()
{
Dispatcher.Invoke(() => Popup.IsOpen = false);
}
private void InitializeKeyboardLayoutControl()
{
var originalBrush = Button.Background;
InitializeLayouts();
keyboard.LayoutChanged += Keyboard_LayoutChanged;
Button.Click += (o, args) =>
{
Popup.IsOpen = !Popup.IsOpen;
Task.Delay(200).ContinueWith(_ => this.Dispatcher.BeginInvoke((System.Action) (() =>
{
((LayoutsStackPanel.Children[0] as ContentControl).Content as UIElement).Focus();
})));
};
var lastOpenedBySpacePress = false;
Button.PreviewKeyDown += (o, args) =>
{
// For some reason, the popup immediately closes again if opened by a Space Bar key event - as a mitigation,
// we record the space bar event and leave the popup open for at least 3 seconds.
if (args.Key == System.Windows.Input.Key.Space)
{
lastOpenedBySpacePress = true;
}
};
Button.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() =>
{
if (Popup.IsOpen && lastOpenedBySpacePress)
{
return;
}
Popup.IsOpen = Popup.IsMouseOver;
}));
Popup.CustomPopupPlacementCallback = new CustomPopupPlacementCallback(Popup_PlacementCallback);
Popup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() =>
{
if (Popup.IsOpen && lastOpenedBySpacePress)
{
return;
}
Popup.IsOpen = IsMouseOver;
}));
Popup.Opened += (o, args) =>
{
Background = Brushes.LightGray;
Button.Background = Brushes.LightGray;
};
Popup.Closed += (o, args) =>
{
Background = originalBrush;
Button.Background = originalBrush;
lastOpenedBySpacePress = false;
};
}
private void Keyboard_LayoutChanged(IKeyboardLayout layout)
{
Dispatcher.InvokeAsync(() => SetCurrent(layout));
}
private CustomPopupPlacement[] Popup_PlacementCallback(Size popupSize, Size targetSize, Point offset)
{
return new[]
{
new CustomPopupPlacement(new Point(targetSize.Width / 2 - popupSize.Width / 2, -popupSize.Height), PopupPrimaryAxis.None)
};
}
private void InitializeLayouts()
{
foreach (var layout in keyboard.GetLayouts())
{
var button = new KeyboardLayoutButton(layout);
button.LayoutSelected += (o, args) => ActivateLayout(layout);
LayoutsStackPanel.Children.Add(button);
if (layout.IsCurrent)
{
SetCurrent(layout);
}
}
}
private void ActivateLayout(IKeyboardLayout layout)
{
Popup.IsOpen = false;
keyboard.ActivateLayout(layout.Id);
}
private void SetCurrent(IKeyboardLayout layout)
{
var name = layout.CultureName?.Length > 3 ? String.Join(string.Empty, layout.CultureName.Split(' ').Where(s => Char.IsLetter(s.First())).Select(s => s.First())) : layout.CultureName;
var tooltip = text.Get(TextKey.SystemControl_KeyboardLayoutTooltip).Replace("%%LAYOUT%%", layout.CultureName);
foreach (var child in LayoutsStackPanel.Children)
{
if (child is KeyboardLayoutButton layoutButton)
{
layoutButton.IsCurrent = layout.Id == layoutButton.LayoutId;
}
}
LayoutCultureCode.Text = layout.CultureCode;
Button.ToolTip = tooltip;
AutomationProperties.SetName(Button, tooltip);
}
private void Popup_KeyUp(object sender, System.Windows.Input.KeyEventArgs e)
{
if (e.Key == System.Windows.Input.Key.Enter || e.Key == System.Windows.Input.Key.Escape)
{
Popup.IsOpen = false;
Button.Focus();
}
}
}
}

View File

@@ -0,0 +1,30 @@
<UserControl x:Class="SafeExamBrowser.UserInterface.Mobile.Controls.Taskbar.NetworkButton" x:ClassModifier="internal"
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"
mc:Ignorable="d" d:DesignHeight="40" d:DesignWidth="250">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="../../Templates/Buttons.xaml" />
<ResourceDictionary Source="../../Templates/Colors.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid>
<Button x:Name="Button" Background="Transparent" Height="60" Padding="10,0" Template="{StaticResource TaskbarButton}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock x:Name="IsCurrentTextBlock" Grid.Column="0" FontSize="20" HorizontalAlignment="Center" VerticalAlignment="Center" Visibility="Hidden">•</TextBlock>
<TextBlock x:Name="SignalStrengthTextBlock" Grid.Column="1" Foreground="Gray" HorizontalAlignment="Left" Margin="10,0,5,0" VerticalAlignment="Center" />
<TextBlock x:Name="NetworkNameTextBlock" Grid.Column="2" FontWeight="Bold" HorizontalAlignment="Left" Margin="5,0,10,0" VerticalAlignment="Center" />
</Grid>
</Button>
</Grid>
</UserControl>

View File

@@ -0,0 +1,43 @@
/*
* Copyright (c) 2024 ETH Zürich, IT Services
*
* 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.Windows;
using System.Windows.Controls;
using SafeExamBrowser.SystemComponents.Contracts.Network;
namespace SafeExamBrowser.UserInterface.Mobile.Controls.Taskbar
{
internal partial class NetworkButton : UserControl
{
private readonly IWirelessNetwork network;
internal event EventHandler NetworkSelected;
internal NetworkButton(IWirelessNetwork network)
{
this.network = network;
InitializeComponent();
InitializeNetworkButton();
}
private void InitializeNetworkButton()
{
Button.Click += (o, args) => NetworkSelected?.Invoke(this, EventArgs.Empty);
IsCurrentTextBlock.Visibility = network.Status == ConnectionStatus.Connected ? Visibility.Visible : Visibility.Hidden;
NetworkNameTextBlock.Text = network.Name;
SignalStrengthTextBlock.Text = $"{network.SignalStrength}%";
}
public void SetFocus()
{
Button.Focus();
}
}
}

View File

@@ -0,0 +1,37 @@
<UserControl x:Class="SafeExamBrowser.UserInterface.Mobile.Controls.Taskbar.NetworkControl" x:ClassModifier="internal"
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:fa="http://schemas.fontawesome.io/icons/"
xmlns:s="clr-namespace:System;assembly=mscorlib"
xmlns:local="clr-namespace:SafeExamBrowser.UserInterface.Mobile.Controls"
mc:Ignorable="d" d:DesignHeight="40" d:DesignWidth="40">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="../../Templates/Buttons.xaml" />
<ResourceDictionary Source="../../Templates/Colors.xaml" />
<ResourceDictionary Source="../../Templates/ScrollViewers.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid>
<Popup x:Name="Popup" IsOpen="False" Placement="Custom" PlacementTarget="{Binding ElementName=Button}">
<Border Background="LightGray" BorderBrush="Gray" BorderThickness="1,1,1,0">
<ScrollViewer MaxHeight="250" VerticalScrollBarVisibility="Auto" Template="{StaticResource SmallBarScrollViewer}">
<StackPanel x:Name="WirelessNetworksStackPanel" />
</ScrollViewer>
</Border>
</Popup>
<Button x:Name="Button" Background="Transparent" Padding="5" Template="{StaticResource TaskbarButton}" ToolTipService.ShowOnDisabled="True" Width="60">
<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
<Viewbox x:Name="WirelessIcon" Stretch="Uniform" Width="Auto" Visibility="Collapsed" />
<fa:ImageAwesome x:Name="WiredIcon" Icon="Tv" Margin="0,2,4,4" Visibility="Collapsed" />
<Border Background="{StaticResource BackgroundBrush}" CornerRadius="6" Height="18" HorizontalAlignment="Right" Margin="0,0,-1,1" Panel.ZIndex="10" VerticalAlignment="Bottom">
<fa:ImageAwesome x:Name="NetworkStatusIcon" />
</Border>
</Grid>
</Button>
</Grid>
</UserControl>

View File

@@ -0,0 +1,188 @@
/*
* Copyright (c) 2024 ETH Zürich, IT Services
*
* 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.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Media;
using FontAwesome.WPF;
using SafeExamBrowser.Core.Contracts.Resources.Icons;
using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.SystemComponents.Contracts.Network;
using SafeExamBrowser.UserInterface.Contracts.Shell;
using SafeExamBrowser.UserInterface.Shared.Utilities;
namespace SafeExamBrowser.UserInterface.Mobile.Controls.Taskbar
{
internal partial class NetworkControl : UserControl, ISystemControl
{
private readonly INetworkAdapter adapter;
private readonly IText text;
internal NetworkControl(INetworkAdapter adapter, IText text)
{
this.adapter = adapter;
this.text = text;
InitializeComponent();
InitializeWirelessNetworkControl();
}
public void Close()
{
Dispatcher.InvokeAsync(() => Popup.IsOpen = false);
}
private void InitializeWirelessNetworkControl()
{
var lastOpenedBySpacePress = false;
var originalBrush = Button.Background;
adapter.Changed += () => Dispatcher.InvokeAsync(Update);
Button.Click += (o, args) => Popup.IsOpen = !Popup.IsOpen;
Button.PreviewKeyDown += (o, args) =>
{
// For some reason, the popup immediately closes again if opened by a Space Bar key event - as a mitigation,
// we record the space bar event and leave the popup open for at least 3 seconds.
if (args.Key == System.Windows.Input.Key.Space)
{
lastOpenedBySpacePress = true;
}
};
Button.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() =>
{
if (Popup.IsOpen && lastOpenedBySpacePress)
{
return;
}
Popup.IsOpen = Popup.IsMouseOver;
}));
Popup.Closed += (o, args) =>
{
adapter.StopWirelessNetworkScanning();
Background = originalBrush;
Button.Background = originalBrush;
lastOpenedBySpacePress = false;
};
Popup.CustomPopupPlacementCallback = new CustomPopupPlacementCallback(Popup_PlacementCallback);
Popup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() =>
{
if (Popup.IsOpen && lastOpenedBySpacePress)
{
return;
}
Popup.IsOpen = IsMouseOver;
}));
Popup.Opened += (o, args) =>
{
adapter.StartWirelessNetworkScanning();
Background = Brushes.LightGray;
Button.Background = Brushes.LightGray;
Task.Delay(100).ContinueWith((task) => Dispatcher.Invoke(() =>
{
if (WirelessNetworksStackPanel.Children.Count > 0)
{
(WirelessNetworksStackPanel.Children[0] as NetworkButton)?.SetFocus();
}
}));
};
WirelessIcon.Child = GetWirelessIcon(0);
Update();
}
private CustomPopupPlacement[] Popup_PlacementCallback(Size popupSize, Size targetSize, Point offset)
{
return new[]
{
new CustomPopupPlacement(new Point(targetSize.Width / 2 - popupSize.Width / 2, -popupSize.Height), PopupPrimaryAxis.None)
};
}
private void Update()
{
switch (adapter.Type)
{
case ConnectionType.Wired:
Button.IsEnabled = false;
UpdateText(text.Get(TextKey.SystemControl_NetworkWiredConnected));
WiredIcon.Visibility = Visibility.Visible;
WirelessIcon.Visibility = Visibility.Collapsed;
break;
case ConnectionType.Wireless:
Button.IsEnabled = true;
WiredIcon.Visibility = Visibility.Collapsed;
WirelessIcon.Visibility = Visibility.Visible;
break;
default:
Button.IsEnabled = false;
UpdateText(text.Get(TextKey.SystemControl_NetworkNotAvailable));
WiredIcon.Visibility = Visibility.Visible;
WirelessIcon.Visibility = Visibility.Collapsed;
break;
}
switch (adapter.Status)
{
case ConnectionStatus.Connected:
UpdateText(text.Get(TextKey.SystemControl_NetworkWiredConnected));
NetworkStatusIcon.Rotation = 0;
NetworkStatusIcon.Source = ImageAwesome.CreateImageSource(FontAwesomeIcon.Globe, Brushes.Green);
NetworkStatusIcon.Spin = false;
break;
case ConnectionStatus.Connecting:
UpdateText(text.Get(TextKey.SystemControl_NetworkWirelessConnecting));
NetworkStatusIcon.Rotation = 0;
NetworkStatusIcon.Source = ImageAwesome.CreateImageSource(FontAwesomeIcon.Cog, Brushes.DimGray);
NetworkStatusIcon.Spin = true;
NetworkStatusIcon.SpinDuration = 2;
break;
default:
UpdateText(text.Get(TextKey.SystemControl_NetworkDisconnected));
NetworkStatusIcon.Source = ImageAwesome.CreateImageSource(FontAwesomeIcon.Ban, Brushes.DarkOrange);
NetworkStatusIcon.Spin = false;
WirelessIcon.Child = GetWirelessIcon(0);
break;
}
WirelessNetworksStackPanel.Children.Clear();
foreach (var network in adapter.GetWirelessNetworks())
{
var button = new NetworkButton(network);
button.NetworkSelected += (o, args) => adapter.ConnectToWirelessNetwork(network.Name);
if (network.Status == ConnectionStatus.Connected)
{
WirelessIcon.Child = GetWirelessIcon(network.SignalStrength);
UpdateText(text.Get(TextKey.SystemControl_NetworkWirelessConnected).Replace("%%NAME%%", network.Name));
}
WirelessNetworksStackPanel.Children.Add(button);
}
}
private void UpdateText(string text)
{
Button.ToolTip = text;
Button.SetValue(System.Windows.Automation.AutomationProperties.NameProperty, text);
}
private UIElement GetWirelessIcon(int signalStrength)
{
var icon = signalStrength > 66 ? "100" : (signalStrength > 33 ? "66" : (signalStrength > 0 ? "33" : "0"));
var uri = new Uri($"pack://application:,,,/SafeExamBrowser.UserInterface.Mobile;component/Images/WiFi_{icon}.xaml");
var resource = new XamlIconResource { Uri = uri };
return IconResourceLoader.Load(resource);
}
}
}

View File

@@ -0,0 +1,20 @@
<UserControl x:Class="SafeExamBrowser.UserInterface.Mobile.Controls.Taskbar.NotificationButton" x:ClassModifier="internal"
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"
mc:Ignorable="d" d:DesignHeight="40" d:DesignWidth="40">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="../../Templates/Buttons.xaml" />
<ResourceDictionary Source="../../Templates/Colors.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid>
<Button x:Name="IconButton" Background="{StaticResource BackgroundBrush}" Click="IconButton_Click" Padding="7.5"
Template="{StaticResource TaskbarButton}" ToolTipService.ShowOnDisabled="True" Width="60" />
</Grid>
</UserControl>

View File

@@ -0,0 +1,53 @@
/*
* Copyright (c) 2024 ETH Zürich, IT Services
*
* 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.Automation;
using System.Windows.Controls;
using SafeExamBrowser.Core.Contracts.Notifications;
using SafeExamBrowser.UserInterface.Contracts.Shell;
using SafeExamBrowser.UserInterface.Shared.Utilities;
namespace SafeExamBrowser.UserInterface.Mobile.Controls.Taskbar
{
internal partial class NotificationButton : UserControl, INotificationControl
{
private readonly INotification notification;
internal NotificationButton(INotification notification)
{
this.notification = notification;
InitializeComponent();
InitializeNotification();
UpdateNotification();
}
private void IconButton_Click(object sender, RoutedEventArgs e)
{
if (notification.CanActivate)
{
notification.Activate();
}
}
private void InitializeNotification()
{
notification.NotificationChanged += () => Dispatcher.Invoke(UpdateNotification);
}
private void UpdateNotification()
{
IconButton.Content = IconResourceLoader.Load(notification.IconResource);
IconButton.IsEnabled = notification.CanActivate;
IconButton.ToolTip = notification.Tooltip;
AutomationProperties.SetName(this, notification.Tooltip);
}
}
}

View File

@@ -0,0 +1,63 @@
<UserControl x:Class="SafeExamBrowser.UserInterface.Mobile.Controls.Taskbar.PowerSupplyControl" x:ClassModifier="internal"
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"
mc:Ignorable="d" d:DesignHeight="40" d:DesignWidth="40" Focusable="True" IsTabStop="True">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="../../Templates/Buttons.xaml" />
<ResourceDictionary Source="../../Templates/Colors.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid>
<Popup x:Name="Popup" IsOpen="False" Placement="Custom" PlacementTarget="{Binding ElementName=Button}">
<Border Background="LightGray" BorderBrush="Gray" BorderThickness="1,1,1,0">
<Grid MaxWidth="250" Margin="20,10,20,20">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Button Grid.Column="1" Background="Transparent" BorderBrush="LightGray" Click="Button_Click" Cursor="Hand" FontWeight="Bold"
Foreground="Gray" HorizontalAlignment="Center" Margin="0,0,0,5" Width="20">X</Button>
</Grid>
<TextBlock Grid.Row="1" x:Name="PopupText" TextWrapping="Wrap" />
</Grid>
</Border>
</Popup>
<Button x:Name="Button" Background="Transparent" IsEnabled="False" Padding="5" Template="{StaticResource TaskbarButton}" ToolTipService.ShowOnDisabled="True">
<Viewbox Stretch="Uniform" Width="Auto">
<Canvas Height="40" Width="40">
<Viewbox Stretch="Uniform" Width="40" Panel.ZIndex="2">
<Canvas Width="1024.000" Height="1024.000">
<Canvas.LayoutTransform>
<RotateTransform Angle="180" />
</Canvas.LayoutTransform>
<Canvas>
<Path Data=" M 0.000,0.000 L 1024.000,0.000 L 1024.000,1024.000 L 0.000,1024.000 L 0.000,0.000 Z"/>
<Path StrokeThickness="35.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 112.000,300.000 L 951.000,300.000 C 970.330,300.000 986.000,315.670 986.000,335.000 L 986.000,689.000 C 986.000,708.330 970.330,724.000 951.000,724.000 L 112.000,724.000 C 92.670,724.000 77.000,708.330 77.000,689.000 L 77.000,335.000 C 77.000,315.670 92.670,300.000 112.000,300.000 Z"/>
<Path StrokeThickness="1.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Fill="#ff000000" Data=" M 25.000,462.500 L 50.000,462.500 C 52.760,462.500 55.000,464.740 55.000,467.500 L 55.000,557.500 C 55.000,560.260 52.760,562.500 50.000,562.500 L 25.000,562.500 C 22.240,562.500 20.000,560.260 20.000,557.500 L 20.000,467.500 C 20.000,464.740 22.240,462.500 25.000,462.500 Z"/>
</Canvas>
</Canvas>
</Viewbox>
<Rectangle x:Name="BatteryCharge" Canvas.Left="2" Canvas.Top="12" Fill="Green" Height="16" Width="35" Panel.ZIndex="1" />
<Canvas x:Name="PowerPlug" Panel.ZIndex="3" Canvas.Left="4" Canvas.Top="-3">
<Canvas.LayoutTransform>
<ScaleTransform ScaleX="2" ScaleY="2" />
</Canvas.LayoutTransform>
<Path Stroke="Black" StrokeStartLineCap="Round" Fill="Black" Data="M2.5,17.5 V10 Q5,10 5,6 H4 V4 H4 V6 H1 V4 H1 V6 H0 Q0,10 2.5,10" />
</Canvas>
<TextBlock x:Name="Warning" FontSize="36" FontWeight="ExtraBold" Foreground="Red" Canvas.Left="13" Canvas.Top="-7" Panel.ZIndex="3">!</TextBlock>
</Canvas>
</Viewbox>
</Button>
</Grid>
</UserControl>

View File

@@ -0,0 +1,155 @@
/*
* Copyright (c) 2024 ETH Zürich, IT Services
*
* 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.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Media;
using System.Windows.Threading;
using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.SystemComponents.Contracts.PowerSupply;
using SafeExamBrowser.UserInterface.Contracts.Shell;
namespace SafeExamBrowser.UserInterface.Mobile.Controls.Taskbar
{
internal partial class PowerSupplyControl : UserControl, ISystemControl
{
private Brush initialBrush;
private bool infoShown, warningShown;
private double maxWidth;
private IPowerSupply powerSupply;
private IText text;
internal PowerSupplyControl(IPowerSupply powerSupply, IText text)
{
this.powerSupply = powerSupply;
this.text = text;
InitializeComponent();
InitializePowerSupplyControl();
}
public void Close()
{
Dispatcher.InvokeAsync(ClosePopup);
}
public void SetInformation(string text)
{
Dispatcher.InvokeAsync(() => PopupText.Text = text);
}
private void InitializePowerSupplyControl()
{
initialBrush = BatteryCharge.Fill;
maxWidth = BatteryCharge.Width;
powerSupply.StatusChanged += PowerSupply_StatusChanged;
Popup.CustomPopupPlacementCallback = new CustomPopupPlacementCallback(Popup_PlacementCallback);
UpdateStatus(powerSupply.GetStatus());
}
private void Button_Click(object sender, RoutedEventArgs e)
{
ClosePopup();
}
private CustomPopupPlacement[] Popup_PlacementCallback(Size popupSize, Size targetSize, Point offset)
{
return new[]
{
new CustomPopupPlacement(new Point(targetSize.Width / 2 - popupSize.Width / 2, -popupSize.Height), PopupPrimaryAxis.None)
};
}
private void PowerSupply_StatusChanged(IPowerSupplyStatus status)
{
Dispatcher.InvokeAsync(() => UpdateStatus(status));
}
private void UpdateStatus(IPowerSupplyStatus status)
{
var percentage = Math.Round(status.BatteryCharge * 100);
var tooltip = string.Empty;
RenderCharge(status.BatteryCharge, status.BatteryChargeStatus);
if (status.IsOnline)
{
infoShown = false;
warningShown = false;
tooltip = text.Get(percentage == 100 ? TextKey.SystemControl_BatteryCharged : TextKey.SystemControl_BatteryCharging);
tooltip = tooltip.Replace("%%CHARGE%%", percentage.ToString());
ClosePopup();
}
else
{
tooltip = text.Get(TextKey.SystemControl_BatteryRemainingCharge);
tooltip = tooltip.Replace("%%CHARGE%%", percentage.ToString());
tooltip = tooltip.Replace("%%HOURS%%", status.BatteryTimeRemaining.Hours.ToString());
tooltip = tooltip.Replace("%%MINUTES%%", status.BatteryTimeRemaining.Minutes.ToString());
HandleBatteryStatus(status.BatteryChargeStatus);
}
Button.ToolTip = tooltip;
PowerPlug.Visibility = status.IsOnline ? Visibility.Visible : Visibility.Collapsed;
Warning.Visibility = status.BatteryChargeStatus == BatteryChargeStatus.Critical ? Visibility.Visible : Visibility.Collapsed;
this.SetValue(System.Windows.Automation.AutomationProperties.NameProperty, tooltip);
}
private void RenderCharge(double charge, BatteryChargeStatus status)
{
var width = maxWidth * charge;
BatteryCharge.Width = width > maxWidth ? maxWidth : (width < 0 ? 0 : width);
switch (status)
{
case BatteryChargeStatus.Critical:
BatteryCharge.Fill = Brushes.Red;
break;
case BatteryChargeStatus.Low:
BatteryCharge.Fill = Brushes.Orange;
break;
default:
BatteryCharge.Fill = initialBrush;
break;
}
}
private void HandleBatteryStatus(BatteryChargeStatus chargeStatus)
{
if (chargeStatus == BatteryChargeStatus.Low && !infoShown)
{
ShowPopup(text.Get(TextKey.SystemControl_BatteryChargeLowInfo));
infoShown = true;
}
if (chargeStatus == BatteryChargeStatus.Critical && !warningShown)
{
ShowPopup(text.Get(TextKey.SystemControl_BatteryChargeCriticalWarning));
warningShown = true;
}
}
private void ShowPopup(string text)
{
Popup.IsOpen = true;
PopupText.Text = text;
Background = Brushes.LightGray;
}
private void ClosePopup()
{
Popup.IsOpen = false;
Background = Brushes.Transparent;
}
}
}

View File

@@ -0,0 +1,22 @@
<UserControl x:Class="SafeExamBrowser.UserInterface.Mobile.Controls.Taskbar.QuitButton" x:ClassModifier="internal"
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"
x:Name="root"
mc:Ignorable="d" d:DesignHeight="40" d:DesignWidth="40">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="../../Templates/Buttons.xaml" />
<ResourceDictionary Source="../../Templates/Colors.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid>
<Button x:Name="Button" Click="Button_Click" Background="{StaticResource BackgroundBrush}" HorizontalAlignment="Stretch"
Template="{StaticResource TaskbarButton}" VerticalAlignment="Stretch"
AutomationProperties.Name="{Binding Path=(AutomationProperties.Name), ElementName=root}" />
</Grid>
</UserControl>

View File

@@ -0,0 +1,42 @@
/*
* Copyright (c) 2024 ETH Zürich, IT Services
*
* 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.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using SafeExamBrowser.Core.Contracts.Resources.Icons;
using SafeExamBrowser.UserInterface.Contracts.Shell.Events;
using SafeExamBrowser.UserInterface.Shared.Utilities;
namespace SafeExamBrowser.UserInterface.Mobile.Controls.Taskbar
{
internal partial class QuitButton : UserControl
{
internal event QuitButtonClickedEventHandler Clicked;
public QuitButton()
{
InitializeComponent();
LoadIcon();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
Clicked?.Invoke(new CancelEventArgs());
}
private void LoadIcon()
{
var uri = new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Desktop;component/Images/ShutDown.xaml");
var resource = new XamlIconResource { Uri = uri };
Button.Content = IconResourceLoader.Load(resource);
}
}
}

View File

@@ -0,0 +1,35 @@
<UserControl x:Class="SafeExamBrowser.UserInterface.Mobile.Controls.Taskbar.RaiseHandControl"
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"
mc:Ignorable="d" d:DesignHeight="40" d:DesignWidth="40">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="../../Templates/Buttons.xaml" />
<ResourceDictionary Source="../../Templates/Colors.xaml" />
<ResourceDictionary Source="../../Templates/ScrollViewers.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid>
<Popup x:Name="Popup" IsOpen="False" Placement="Custom" PlacementTarget="{Binding ElementName=Button}">
<Border Background="LightGray" BorderBrush="Gray" BorderThickness="1,1,1,0" >
<StackPanel>
<TextBox Name="Message" AcceptsReturn="True" Height="150" IsReadOnly="False" Margin="5,5,5,0" Width="350" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" />
<Grid>
<Button Name="HandButton" Background="Transparent" Height="30" Margin="5" Padding="5" Template="{StaticResource TaskbarButton}" Width="150">
<Viewbox Stretch="Uniform">
<TextBlock x:Name="HandButtonText" FontWeight="Bold" TextAlignment="Center" />
</Viewbox>
</Button>
</Grid>
</StackPanel>
</Border>
</Popup>
<Button x:Name="NotificationButton" Background="Transparent" Template="{StaticResource TaskbarButton}" Padding="5" Width="60">
<ContentControl Name="Icon" />
</Button>
</Grid>
</UserControl>

View File

@@ -0,0 +1,161 @@
/*
* Copyright (c) 2024 ETH Zürich, IT Services
*
* 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.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Media;
using SafeExamBrowser.Core.Contracts.Resources.Icons;
using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.Proctoring.Contracts;
using SafeExamBrowser.Settings.Proctoring;
using SafeExamBrowser.UserInterface.Contracts.Shell;
using SafeExamBrowser.UserInterface.Shared.Utilities;
namespace SafeExamBrowser.UserInterface.Mobile.Controls.Taskbar
{
public partial class RaiseHandControl : UserControl, INotificationControl
{
private readonly IProctoringController controller;
private readonly ProctoringSettings settings;
private readonly IText text;
private IconResource LoweredIcon;
private IconResource RaisedIcon;
public RaiseHandControl(IProctoringController controller, ProctoringSettings settings, IText text)
{
this.controller = controller;
this.settings = settings;
this.text = text;
InitializeComponent();
InitializeRaiseHandControl();
}
private void InitializeRaiseHandControl()
{
var originalBrush = NotificationButton.Background;
controller.HandLowered += () => Dispatcher.Invoke(ShowLowered);
controller.HandRaised += () => Dispatcher.Invoke(ShowRaised);
HandButton.Click += RaiseHandButton_Click;
HandButtonText.Text = text.Get(TextKey.Notification_ProctoringRaiseHand);
LoweredIcon = new XamlIconResource { Uri = new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Desktop;component/Images/Hand_Lowered.xaml") };
RaisedIcon = new XamlIconResource { Uri = new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Desktop;component/Images/Hand_Raised.xaml") };
Icon.Content = IconResourceLoader.Load(LoweredIcon);
var lastOpenedBySpacePress = false;
NotificationButton.PreviewKeyDown += (o, args) =>
{
if (args.Key == System.Windows.Input.Key.Space) // for some reason, the popup immediately closes again if opened by a Space Bar key event - as a mitigation, we record the space bar event and leave the popup open for at least 3 seconds
{
lastOpenedBySpacePress = true;
}
};
NotificationButton.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() =>
{
if (Popup.IsOpen && lastOpenedBySpacePress)
{
return;
}
Popup.IsOpen = Popup.IsMouseOver;
}));
NotificationButton.PreviewMouseLeftButtonUp += NotificationButton_PreviewMouseLeftButtonUp;
NotificationButton.PreviewMouseRightButtonUp += NotificationButton_PreviewMouseRightButtonUp;
NotificationButton.ToolTip = text.Get(TextKey.Notification_ProctoringHandLowered);
Popup.CustomPopupPlacementCallback = new CustomPopupPlacementCallback(Popup_PlacementCallback);
Popup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() =>
{
if (Popup.IsOpen && lastOpenedBySpacePress)
{
return;
}
Popup.IsOpen = IsMouseOver;
}));
Popup.Opened += (o, args) =>
{
Background = Brushes.LightGray;
NotificationButton.Background = Brushes.LightGray;
};
Popup.Closed += (o, args) =>
{
Background = originalBrush;
NotificationButton.Background = originalBrush;
lastOpenedBySpacePress = false;
};
}
private void NotificationButton_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if (settings.ForceRaiseHandMessage || Popup.IsOpen)
{
Popup.IsOpen = !Popup.IsOpen;
}
else
{
ToggleHand();
}
}
private void NotificationButton_PreviewMouseRightButtonUp(object sender, MouseButtonEventArgs e)
{
Popup.IsOpen = !Popup.IsOpen;
}
private CustomPopupPlacement[] Popup_PlacementCallback(Size popupSize, Size targetSize, Point offset)
{
return new[]
{
new CustomPopupPlacement(new Point(targetSize.Width / 2 - popupSize.Width / 2, -popupSize.Height), PopupPrimaryAxis.None)
};
}
private void RaiseHandButton_Click(object sender, RoutedEventArgs e)
{
ToggleHand();
}
private void ToggleHand()
{
if (controller.IsHandRaised)
{
controller.LowerHand();
}
else
{
controller.RaiseHand(Message.Text);
Message.Clear();
}
}
private void ShowLowered()
{
HandButtonText.Text = text.Get(TextKey.Notification_ProctoringRaiseHand);
Icon.Content = IconResourceLoader.Load(LoweredIcon);
Message.IsEnabled = true;
NotificationButton.ToolTip = text.Get(TextKey.Notification_ProctoringHandLowered);
Popup.IsOpen = false;
}
private void ShowRaised()
{
HandButtonText.Text = text.Get(TextKey.Notification_ProctoringLowerHand);
Icon.Content = IconResourceLoader.Load(RaisedIcon);
Message.IsEnabled = false;
NotificationButton.ToolTip = text.Get(TextKey.Notification_ProctoringHandRaised);
}
}
}

View File

@@ -0,0 +1,31 @@
<UserControl x:Class="SafeExamBrowser.UserInterface.Mobile.Controls.Taskview.WindowControl" x:ClassModifier="internal"
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"
FontSize="16">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="../../Templates/Colors.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid Margin="5" Height="200" Width="300">
<Border Name="Indicator" Background="{StaticResource BackgroundTransparentEmphasisBrush}" BorderThickness="0" Visibility="Hidden" />
<Grid Margin="5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<ContentControl Grid.Row="0" Grid.Column="0" Name="Icon" Margin="0,0,5,0" Height="24" />
<TextBlock Grid.Row="0" Grid.Column="1" Name="Title" Foreground="{StaticResource PrimaryTextBrush}" VerticalAlignment="Center" TextTrimming="CharacterEllipsis" />
<ContentControl Grid.Row="1" Grid.ColumnSpan="2" Name="Placeholder" Margin="0,5,0,0" />
</Grid>
</Grid>
</UserControl>

View File

@@ -0,0 +1,164 @@
/*
* Copyright (c) 2024 ETH Zürich, IT Services
*
* 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.Windows;
using System.Windows.Controls;
using SafeExamBrowser.Applications.Contracts;
using SafeExamBrowser.Core.Contracts.Resources.Icons;
using SafeExamBrowser.UserInterface.Shared.Utilities;
namespace SafeExamBrowser.UserInterface.Mobile.Controls.Taskview
{
internal partial class WindowControl : UserControl
{
private Windows.Taskview taskview;
private IntPtr thumbnail;
private IApplicationWindow window;
internal WindowControl(IApplicationWindow window, Windows.Taskview taskview)
{
this.window = window;
this.taskview = taskview;
InitializeComponent();
InitializeControl();
}
internal void Activate()
{
window.Activate();
}
internal void Deselect()
{
Indicator.Visibility = Visibility.Hidden;
Title.FontWeight = FontWeights.Normal;
}
internal void Destroy()
{
if (thumbnail != IntPtr.Zero)
{
Thumbnail.DwmUnregisterThumbnail(thumbnail);
}
}
internal void Select()
{
Indicator.Visibility = Visibility.Visible;
Title.FontWeight = FontWeights.SemiBold;
}
internal void Update()
{
if (!IsLoaded || !IsVisible)
{
return;
}
if (thumbnail == IntPtr.Zero && taskview.Handle != IntPtr.Zero && window.Handle != IntPtr.Zero)
{
Thumbnail.DwmRegisterThumbnail(taskview.Handle, window.Handle, out thumbnail);
}
if (thumbnail != IntPtr.Zero)
{
Thumbnail.DwmQueryThumbnailSourceSize(thumbnail, out var size);
var destination = CalculatePhysicalDestination(size);
var properties = new Thumbnail.Properties
{
Destination = destination,
Flags = Thumbnail.DWM_TNP_RECTDESTINATION | Thumbnail.DWM_TNP_VISIBLE,
Visible = true
};
Thumbnail.DwmUpdateThumbnailProperties(thumbnail, ref properties);
}
}
private void TaskViewWindowControl_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if (e.NewValue as bool? == true)
{
Update();
}
}
private void Window_TitleChanged(string title)
{
Dispatcher.InvokeAsync(() => Title.Text = title);
}
private void Window_IconChanged(IconResource icon)
{
Dispatcher.InvokeAsync(() => Icon.Content = IconResourceLoader.Load(window.Icon));
}
private Thumbnail.Rectangle CalculatePhysicalDestination(Thumbnail.Size size)
{
var controlToTaskview = TransformToVisual(taskview);
var placeholderToControl = Placeholder.TransformToVisual(this);
var placeholderLeft = placeholderToControl.Transform(new Point(0, 0)).X;
var placeholderTop = placeholderToControl.Transform(new Point(0, 0)).Y;
var placeholderRight = placeholderToControl.Transform(new Point(Placeholder.ActualWidth, 0)).X;
var placeholderBottom = placeholderToControl.Transform(new Point(0, Placeholder.ActualHeight)).Y;
var physicalBounds = new Thumbnail.Rectangle
{
Left = (int) Math.Round(this.TransformToPhysical(controlToTaskview.Transform(new Point(placeholderLeft, 0)).X, 0).X),
Top = (int) Math.Round(this.TransformToPhysical(0, controlToTaskview.Transform(new Point(0, placeholderTop)).Y).Y),
Right = (int) Math.Round(this.TransformToPhysical(controlToTaskview.Transform(new Point(placeholderRight, 0)).X, 0).X),
Bottom = (int) Math.Round(this.TransformToPhysical(0, controlToTaskview.Transform(new Point(0, placeholderBottom)).Y).Y)
};
var scaleFactor = default(double);
var thumbnailHeight = default(double);
var thumbnailWidth = default(double);
var maxWidth = (double) physicalBounds.Right - physicalBounds.Left;
var maxHeight = (double) physicalBounds.Bottom - physicalBounds.Top;
var placeholderRatio = maxWidth / maxHeight;
var windowRatio = (double) size.X / size.Y;
if (windowRatio < placeholderRatio)
{
thumbnailHeight = maxHeight;
scaleFactor = thumbnailHeight / size.Y;
thumbnailWidth = size.X * scaleFactor;
}
else
{
thumbnailWidth = maxWidth;
scaleFactor = thumbnailWidth / size.X;
thumbnailHeight = size.Y * scaleFactor;
}
var widthDifference = maxWidth - thumbnailWidth;
var heightDifference = maxHeight - thumbnailHeight;
return new Thumbnail.Rectangle
{
Left = (int) Math.Round(physicalBounds.Left + (widthDifference / 2)),
Top = (int) Math.Round(physicalBounds.Top + (heightDifference / 2)),
Right = (int) Math.Round(physicalBounds.Right - (widthDifference / 2)),
Bottom = (int) Math.Round(physicalBounds.Bottom - (heightDifference / 2))
};
}
private void InitializeControl()
{
Icon.Content = IconResourceLoader.Load(window.Icon);
IsVisibleChanged += TaskViewWindowControl_IsVisibleChanged;
Loaded += (o, args) => Update();
Title.Text = window.Title;
window.IconChanged += Window_IconChanged;
window.TitleChanged += Window_TitleChanged;
}
}
}

View File

@@ -0,0 +1,49 @@
/*
* Copyright (c) 2024 ETH Zürich, IT Services
*
* 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.SystemComponents.Contracts;
using SafeExamBrowser.UserInterface.Contracts.FileSystemDialog;
using SafeExamBrowser.UserInterface.Contracts.Windows;
using SafeExamBrowser.UserInterface.Mobile.Windows;
namespace SafeExamBrowser.UserInterface.Mobile
{
public class FileSystemDialogFactory : IFileSystemDialog
{
private readonly ISystemInfo systemInfo;
private readonly IText text;
public FileSystemDialogFactory(ISystemInfo systemInfo, IText text)
{
this.systemInfo = systemInfo;
this.text = text;
}
public FileSystemDialogResult Show(
FileSystemElement element,
FileSystemOperation operation,
string initialPath = default,
string message = default,
string title = default,
IWindow parent = default,
bool restrictNavigation = false,
bool showElementPath = true)
{
if (parent is Window window)
{
return window.Dispatcher.Invoke(() => new FileSystemDialog(element, operation, systemInfo, text, initialPath, message, title, parent, restrictNavigation, showElementPath).Show());
}
else
{
return new FileSystemDialog(element, operation, systemInfo, text, initialPath, message, title, restrictNavigation: restrictNavigation, showElementPath: showElementPath).Show();
}
}
}
}

View File

@@ -0,0 +1,14 @@
<Viewbox
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Canvas Width="1024.000" Height="1024.000">
<Canvas>
<Path Data=" M 0.000,0.000 L 1024.000,0.000 L 1024.000,1024.000 L 0.000,1024.000 L 0.000,0.000 Z"/>
<Path StrokeThickness="70.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 117.000,512.000 C 117.000,293.840 293.840,117.000 512.000,117.000 C 730.160,117.000 907.000,293.840 907.000,512.000 C 907.000,730.160 730.160,907.000 512.000,907.000 C 293.840,907.000 117.000,730.160 117.000,512.000 Z"/>
<Path StrokeThickness="70.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 619.210,298.700"/>
<Path StrokeThickness="70.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 619.210,727.560"/>
<Path StrokeThickness="70.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 512.000,458.096 L 512.000,741.560"/>
<Path Fill="#ff000000" Data=" M 547.625,312.700 C 547.625,332.375 531.675,348.325 512.000,348.325 C 492.325,348.325 476.375,332.375 476.375,312.700 C 476.375,293.025 492.325,277.075 512.000,277.075 C 531.675,277.075 547.625,293.025 547.625,312.700 Z"/>
</Canvas>
</Canvas>
</Viewbox>

View File

@@ -0,0 +1,13 @@
<Viewbox
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:fa="http://schemas.fontawesome.io/icons/"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid>
<fa:ImageAwesome Icon="VolumeOff" HorizontalAlignment="Left" />
<Canvas Width="100" Height="100">
<Path StrokeThickness="5.0" Stroke="Black" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data="M 55,35 C 65,45 65,55 55,65"/>
<Path StrokeThickness="5.0" Stroke="Black" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data="M 65,25 C 80,35 80,65 65,75"/>
<Path StrokeThickness="5.0" Stroke="Black" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data="M 75,15 C 95,30 95,70 75,85"/>
</Canvas>
</Grid>
</Viewbox>

View File

@@ -0,0 +1,13 @@
<Viewbox
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:fa="http://schemas.fontawesome.io/icons/"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid>
<fa:ImageAwesome Icon="VolumeOff" HorizontalAlignment="Left" />
<Canvas Width="100" Height="100">
<Path StrokeThickness="5.0" Stroke="Black" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data="M 55,35 C 65,45 65,55 55,65"/>
<Path StrokeThickness="5.0" Stroke="DarkGray" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data="M 65,25 C 80,35 80,65 65,75"/>
<Path StrokeThickness="5.0" Stroke="DarkGray" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data="M 75,15 C 95,30 95,70 75,85"/>
</Canvas>
</Grid>
</Viewbox>

View File

@@ -0,0 +1,13 @@
<Viewbox
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:fa="http://schemas.fontawesome.io/icons/"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid>
<fa:ImageAwesome Icon="VolumeOff" HorizontalAlignment="Left" />
<Canvas Width="100" Height="100">
<Path StrokeThickness="5.0" Stroke="Black" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data="M 55,35 C 65,45 65,55 55,65"/>
<Path StrokeThickness="5.0" Stroke="Black" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data="M 65,25 C 80,35 80,65 65,75"/>
<Path StrokeThickness="5.0" Stroke="DarkGray" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data="M 75,15 C 95,30 95,70 75,85"/>
</Canvas>
</Grid>
</Viewbox>

View File

@@ -0,0 +1,13 @@
<Viewbox
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:fa="http://schemas.fontawesome.io/icons/"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid>
<fa:ImageAwesome Icon="VolumeOff" HorizontalAlignment="Left" />
<Canvas Width="100" Height="100">
<Path StrokeThickness="5.0" Stroke="Black" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data="M 55,35 C 65,45 65,55 55,65"/>
<Path StrokeThickness="5.0" Stroke="Black" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data="M 65,25 C 80,35 80,65 65,75"/>
<Path StrokeThickness="5.0" Stroke="Black" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data="M 75,15 C 95,30 95,70 75,85"/>
</Canvas>
</Grid>
</Viewbox>

View File

@@ -0,0 +1,13 @@
<Viewbox
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:fa="http://schemas.fontawesome.io/icons/"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid>
<fa:ImageAwesome Icon="VolumeOff" HorizontalAlignment="Left" />
<Canvas Width="100" Height="100">
<Path StrokeThickness="5.0" Stroke="Black" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data="M 55,35 C 65,45 65,55 55,65"/>
<Path StrokeThickness="5.0" Stroke="#ffdddddd" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data="M 65,25 C 80,35 80,65 65,75"/>
<Path StrokeThickness="5.0" Stroke="#ffdddddd" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data="M 75,15 C 95,30 95,70 75,85"/>
</Canvas>
</Grid>
</Viewbox>

View File

@@ -0,0 +1,13 @@
<Viewbox
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:fa="http://schemas.fontawesome.io/icons/"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid>
<fa:ImageAwesome Icon="VolumeOff" HorizontalAlignment="Left" />
<Canvas Width="100" Height="100">
<Path StrokeThickness="5.0" Stroke="Black" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data="M 55,35 C 65,45 65,55 55,65"/>
<Path StrokeThickness="5.0" Stroke="Black" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data="M 65,25 C 80,35 80,65 65,75"/>
<Path StrokeThickness="5.0" Stroke="#ffdddddd" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data="M 75,15 C 95,30 95,70 75,85"/>
</Canvas>
</Grid>
</Viewbox>

View File

@@ -0,0 +1,16 @@
<Viewbox
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:fa="http://schemas.fontawesome.io/icons/"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid Margin="1">
<Grid Margin="5,0,0,0">
<fa:ImageAwesome Icon="VolumeOff" Foreground="#ffdddddd" HorizontalAlignment="Left" />
<Canvas Width="100" Height="100">
<Path StrokeThickness="5.0" Stroke="#ffdddddd" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data="M 55,35 C 65,45 65,55 55,65"/>
<Path StrokeThickness="5.0" Stroke="#ffdddddd" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data="M 65,25 C 80,35 80,65 65,75"/>
<Path StrokeThickness="5.0" Stroke="#ffdddddd" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data="M 75,15 C 95,30 95,70 75,85"/>
</Canvas>
</Grid>
<fa:ImageAwesome Foreground="Red" Icon="Ban" Opacity="0.3" />
</Grid>
</Viewbox>

View File

@@ -0,0 +1,12 @@
<Viewbox
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:fa="http://schemas.fontawesome.io/icons/"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid>
<fa:ImageAwesome Icon="VolumeOff" HorizontalAlignment="Left" />
<Canvas Width="100" Height="100">
<Path StrokeThickness="5.0" Stroke="Black" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data="M 60,35 L 90,65"/>
<Path StrokeThickness="5.0" Stroke="Black" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data="M 60,65 L 90,35"/>
</Canvas>
</Grid>
</Viewbox>

View File

@@ -0,0 +1,16 @@
<Viewbox
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:fa="http://schemas.fontawesome.io/icons/"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid Margin="1">
<Grid Margin="5,0,0,0">
<fa:ImageAwesome Icon="VolumeOff" Foreground="DarkGray" HorizontalAlignment="Left" />
<Canvas Width="100" Height="100">
<Path StrokeThickness="5.0" Stroke="DarkGray" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data="M 55,35 C 65,45 65,55 55,65"/>
<Path StrokeThickness="5.0" Stroke="DarkGray" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data="M 65,25 C 80,35 80,65 65,75"/>
<Path StrokeThickness="5.0" Stroke="DarkGray" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data="M 75,15 C 95,30 95,70 75,85"/>
</Canvas>
</Grid>
<fa:ImageAwesome Foreground="Red" Icon="Ban" Opacity="0.3" />
</Grid>
</Viewbox>

View File

@@ -0,0 +1,11 @@
<Viewbox
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Canvas Width="1024.000" Height="1024.000">
<Canvas>
<Path Data=" M 0.000,0.000 L 1024.000,0.000 L 1024.000,1024.000 L 0.000,1024.000 L 0.000,0.000 Z"/>
<Path StrokeThickness="35.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 112.000,300.000 L 951.000,300.000 C 970.330,300.000 986.000,315.670 986.000,335.000 L 986.000,689.000 C 986.000,708.330 970.330,724.000 951.000,724.000 L 112.000,724.000 C 92.670,724.000 77.000,708.330 77.000,689.000 L 77.000,335.000 C 77.000,315.670 92.670,300.000 112.000,300.000 Z"/>
<Path StrokeThickness="1.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Fill="#ff000000" Data=" M 25.000,462.500 L 50.000,462.500 C 52.760,462.500 55.000,464.740 55.000,467.500 L 55.000,557.500 C 55.000,560.260 52.760,562.500 50.000,562.500 L 25.000,562.500 C 22.240,562.500 20.000,560.260 20.000,557.500 L 20.000,467.500 C 20.000,464.740 22.240,462.500 25.000,462.500 Z"/>
</Canvas>
</Canvas>
</Viewbox>

View File

@@ -0,0 +1,9 @@
<Viewbox
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid>
<Canvas Width="858.999" Height="862.000">
<Path Fill="Black" Data="F1 M 425.701,794.995 C 379.929,794.995 340.560,785.583 307.594,766.759 C 274.629,747.934 249.228,720.533 231.394,684.556 C 213.560,648.578 204.643,605.004 204.643,553.835 L 204.643,232.478 C 204.643,225.177 206.828,219.235 211.200,214.654 C 215.571,210.074 221.301,207.783 228.391,207.783 C 235.437,207.783 241.250,210.074 245.831,214.654 C 250.412,219.235 252.702,225.177 252.702,232.478 L 252.702,429.758 C 252.702,437.762 255.532,444.401 261.193,449.677 C 266.854,454.952 273.347,457.590 280.672,457.590 C 288.464,457.590 295.231,454.952 300.971,449.677 C 306.712,444.401 309.582,437.762 309.582,429.758 L 309.582,134.006 C 309.582,126.704 311.704,120.710 315.947,116.024 C 320.191,111.337 325.860,108.994 332.954,108.994 C 340.047,108.994 345.884,111.337 350.465,116.024 C 355.046,120.710 357.336,126.704 357.336,134.006 L 357.336,409.800 C 357.336,417.804 360.153,424.443 365.788,429.719 C 371.423,434.994 378.033,437.632 385.618,437.632 C 393.198,437.632 399.848,434.994 405.566,429.719 C 411.285,424.443 414.145,417.804 414.145,409.800 L 414.145,85.449 C 414.145,78.195 416.435,72.173 421.015,67.383 C 425.596,62.593 431.434,60.198 438.527,60.198 C 445.410,60.198 451.088,62.593 455.563,67.383 C 460.038,72.173 462.276,78.195 462.276,85.449 L 462.276,405.459 C 462.276,413.203 465.042,419.777 470.575,425.180 C 476.108,430.583 482.770,433.285 490.563,433.285 C 498.308,433.285 504.998,430.583 510.635,425.180 C 516.271,419.777 519.089,413.203 519.089,405.459 L 519.089,134.006 C 519.089,126.704 521.304,120.710 525.733,116.024 C 530.163,111.337 535.924,108.994 543.017,108.994 C 550.111,108.994 555.908,111.337 560.407,116.024 C 564.906,120.710 567.155,126.704 567.155,134.006 L 567.155,515.518 C 567.155,526.396 570.239,534.828 576.407,540.814 C 582.574,546.800 590.147,549.793 599.125,549.793 C 606.758,549.749 613.863,547.732 620.440,543.742 C 627.016,539.752 632.568,532.671 637.095,522.500 L 692.280,399.003 C 695.582,391.355 700.202,386.286 706.142,383.797 C 712.082,381.308 718.005,381.203 723.909,383.483 C 730.492,386.062 734.788,390.327 736.798,396.279 C 738.808,402.231 738.336,409.407 735.382,417.806 L 664.082,614.392 C 640.346,679.569 608.514,725.963 568.587,753.576 C 528.659,781.189 481.030,794.995 425.701,794.995 Z M 427.827,859.330 C 498.493,859.330 558.389,840.488 607.515,802.806 C 656.640,765.124 694.482,709.031 721.040,634.529 L 789.323,442.896 C 794.241,429.083 796.700,415.750 796.700,402.896 C 796.700,381.287 789.469,363.388 775.007,349.199 C 760.550,335.009 742.647,327.914 721.298,327.914 C 707.645,327.914 694.945,331.775 683.196,339.498 C 671.448,347.220 662.334,358.505 655.855,373.351 L 635.083,424.458 C 634.364,426.107 633.285,426.932 631.844,426.932 C 630.147,426.932 629.298,425.874 629.298,423.758 L 629.298,128.275 C 629.298,104.087 622.123,84.794 607.772,70.395 C 593.421,55.996 574.457,48.796 550.880,48.796 C 543.511,48.796 536.562,49.966 530.033,52.305 C 523.504,54.645 517.625,58.024 512.395,62.443 C 508.739,43.046 500.446,27.789 487.518,16.674 C 474.590,5.558 458.374,0.000 438.868,0.000 C 419.667,0.000 403.510,5.610 390.396,16.830 C 377.282,28.049 368.767,43.020 364.851,61.742 C 355.210,53.112 343.534,48.796 329.821,48.796 C 307.913,48.796 290.190,55.788 276.651,69.772 C 263.112,83.756 256.343,102.040 256.343,124.622 L 256.343,160.747 C 251.556,156.209 245.815,152.730 239.118,150.310 C 232.422,147.891 225.401,146.682 218.056,146.682 C 196.148,146.682 178.245,153.968 164.347,168.541 C 150.448,183.114 143.499,202.087 143.499,225.460 L 143.499,560.451 C 143.499,622.072 155.334,675.223 179.002,719.906 C 202.671,764.588 235.828,798.996 278.474,823.130 C 321.121,847.263 370.905,859.330 427.827,859.330 Z"/>
</Canvas>
</Grid>
</Viewbox>

View File

@@ -0,0 +1,12 @@
<Viewbox
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid>
<Canvas Width="929.832" Height="936.000">
<Canvas>
<Path Fill="#ff000000" Data="F1 M 188.249,869.559 C 168.871,869.559 153.644,854.794 153.644,835.416 C 153.644,816.960 169.332,801.734 188.249,801.734 C 207.628,801.734 222.854,816.960 222.854,835.416 C 222.854,854.794 207.628,869.559 188.249,869.559 Z M 188.249,777.280 C 173.023,777.280 162.411,767.129 161.949,752.826 L 158.720,655.933 C 158.259,638.861 170.255,626.865 188.249,626.865 C 206.243,626.865 218.240,638.861 217.778,655.933 L 214.549,752.826 C 214.087,767.129 203.015,777.280 188.249,777.280 Z M 188.249,936.000 C 291.603,936.000 376.499,851.103 376.499,747.751 C 376.499,644.398 291.603,559.501 188.249,559.501 C 84.897,559.501 0.000,644.398 0.000,747.751 C 0.000,851.103 84.897,936.000 188.249,936.000 Z"/>
<Path Fill="#ff000000" Data="F1 M 845.839,349.199 C 831.382,335.009 813.479,327.914 792.130,327.914 C 778.478,327.914 765.777,331.775 754.028,339.498 C 742.280,347.220 733.167,358.505 726.688,373.351 L 705.915,424.458 C 705.197,426.107 704.117,426.932 702.676,426.932 C 700.979,426.932 700.131,425.874 700.131,423.758 L 700.131,128.275 C 700.131,104.088 692.955,84.794 678.604,70.395 C 664.253,55.996 645.289,48.796 621.712,48.796 C 614.343,48.796 607.394,49.966 600.865,52.305 C 594.336,54.645 588.457,58.024 583.228,62.443 C 579.571,43.046 571.278,27.789 558.350,16.674 C 545.423,5.558 529.206,0.000 509.701,0.000 C 490.500,0.000 474.342,5.610 461.228,16.830 C 448.115,28.049 439.600,43.020 435.683,61.742 C 426.043,53.112 414.366,48.796 400.654,48.796 C 378.746,48.796 361.022,55.788 347.483,69.772 C 333.945,83.756 327.175,102.040 327.175,124.622 L 327.175,160.747 C 322.389,156.209 316.647,152.730 309.951,150.310 C 303.254,147.891 296.234,146.682 288.888,146.682 C 266.980,146.682 249.077,153.968 235.179,168.541 C 221.281,183.114 214.332,202.087 214.332,225.460 L 214.332,508.446 C 235.287,510.924 255.750,516.158 275.475,524.104 L 275.475,232.478 C 275.475,225.177 277.661,219.235 282.032,214.654 C 286.403,210.074 292.133,207.783 299.223,207.783 C 306.269,207.783 312.083,210.074 316.664,214.654 C 321.244,219.235 323.534,225.177 323.534,232.478 L 323.534,429.758 C 323.534,437.762 326.365,444.401 332.026,449.677 C 337.686,454.952 344.179,457.590 351.504,457.590 C 359.297,457.590 366.063,454.952 371.804,449.677 C 377.544,444.401 380.414,437.762 380.414,429.758 L 380.414,134.006 C 380.414,126.704 382.536,120.710 386.780,116.024 C 391.023,111.337 396.692,108.994 403.786,108.994 C 410.879,108.994 416.717,111.337 421.298,116.024 C 425.878,120.710 428.168,126.704 428.168,134.006 L 428.168,409.800 C 428.168,417.804 430.986,424.443 436.620,429.719 C 442.255,434.994 448.865,437.632 456.450,437.632 C 464.031,437.632 470.680,434.994 476.399,429.719 C 482.117,424.443 484.977,417.804 484.977,409.800 L 484.977,85.449 C 484.977,78.195 487.267,72.173 491.848,67.383 C 496.429,62.593 502.266,60.198 509.359,60.198 C 516.242,60.198 521.921,62.593 526.396,67.383 C 530.871,72.173 533.108,78.195 533.108,85.449 L 533.108,405.459 C 533.108,413.203 535.875,419.777 541.407,425.180 C 546.940,430.583 553.603,433.285 561.395,433.285 C 569.140,433.285 575.830,430.583 581.467,425.180 C 587.104,419.777 589.922,413.203 589.922,405.459 L 589.922,134.006 C 589.922,126.704 592.136,120.710 596.566,116.024 C 600.995,111.337 606.756,108.994 613.850,108.994 C 620.944,108.994 626.740,111.337 631.239,116.024 C 635.738,120.710 637.987,126.704 637.987,134.006 L 637.987,515.518 C 637.987,526.396 641.071,534.828 647.239,540.814 C 653.407,546.800 660.979,549.793 669.958,549.793 C 677.590,549.749 684.695,547.732 691.272,543.742 C 697.849,539.752 703.400,532.671 707.927,522.500 L 763.112,399.003 C 766.414,391.355 771.035,386.286 776.974,383.797 C 782.915,381.308 788.837,381.203 794.741,383.483 C 801.324,386.062 805.620,390.327 807.630,396.279 C 809.640,402.231 809.168,409.406 806.214,417.806 L 734.915,614.392 C 711.179,679.569 679.347,725.963 639.419,753.576 C 599.491,781.189 551.862,794.995 496.533,794.995 C 470.493,794.995 446.532,791.938 424.637,785.846 C 421.591,805.184 416.182,824.071 408.423,842.300 C 407.750,843.879 407.056,845.446 406.352,847.008 C 434.695,855.217 465.461,859.330 498.659,859.330 C 569.325,859.330 629.221,840.488 678.347,802.806 C 727.472,765.124 765.314,709.031 791.873,634.529 L 860.155,442.896 C 865.074,429.083 867.533,415.750 867.533,402.896 C 867.533,381.287 860.302,363.388 845.839,349.199 Z"/>
</Canvas>
</Canvas>
</Grid>
</Viewbox>

View File

@@ -0,0 +1,11 @@
<Viewbox
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Canvas Width="1024.000" Height="1024.000">
<Canvas>
<Path Data=" M 0.000,0.000 L 1024.000,0.000 L 1024.000,1024.000 L 0.000,1024.000 L 0.000,0.000 Z"/>
<Path StrokeThickness="70.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 301.086,510.424 L 786.763,188.341 L 786.763,835.919 L 301.086,510.424 Z"/>
<Path StrokeThickness="70.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data="F1 M 235.016,188.341 L 235.016,835.919"/>
</Canvas>
</Canvas>
</Viewbox>

View File

@@ -0,0 +1,34 @@
<Viewbox
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Canvas Width="1024.000" Height="1024.000">
<Canvas>
<Path Data=" M 0.000,0.000 L 1024.000,0.000 L 1024.000,1024.000 L 0.000,1024.000 L 0.000,0.000 Z"/>
<Path StrokeThickness="35.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 134.000,339.000 L 889.000,339.000 C 908.330,339.000 924.000,354.670 924.000,374.000 L 924.000,649.000 C 924.000,668.330 908.330,684.000 889.000,684.000 L 134.000,684.000 C 114.670,684.000 99.000,668.330 99.000,649.000 L 99.000,374.000 C 99.000,354.670 114.670,339.000 134.000,339.000 Z"/>
<Path StrokeThickness="1.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Fill="#ff000000" Data=" M 152.000,582.000 L 192.000,582.000 C 194.760,582.000 197.000,584.240 197.000,587.000 L 197.000,627.000 C 197.000,629.760 194.760,632.000 192.000,632.000 L 152.000,632.000 C 149.240,632.000 147.000,629.760 147.000,627.000 L 147.000,587.000 C 147.000,584.240 149.240,582.000 152.000,582.000 Z"/>
<Path StrokeThickness="1.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Fill="#ff000000" Data=" M 747.000,582.000 L 787.000,582.000 C 789.760,582.000 792.000,584.240 792.000,587.000 L 792.000,627.000 C 792.000,629.760 789.760,632.000 787.000,632.000 L 747.000,632.000 C 744.240,632.000 742.000,629.760 742.000,627.000 L 742.000,587.000 C 742.000,584.240 744.240,582.000 747.000,582.000 Z"/>
<Path StrokeThickness="1.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Fill="#ff000000" Data=" M 152.000,487.000 L 192.000,487.000 C 194.760,487.000 197.000,489.240 197.000,492.000 L 197.000,532.000 C 197.000,534.760 194.760,537.000 192.000,537.000 L 152.000,537.000 C 149.240,537.000 147.000,534.760 147.000,532.000 L 147.000,492.000 C 147.000,489.240 149.240,487.000 152.000,487.000 Z"/>
<Path StrokeThickness="1.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Fill="#ff000000" Data=" M 322.000,487.000 L 362.000,487.000 C 364.760,487.000 367.000,489.240 367.000,492.000 L 367.000,532.000 C 367.000,534.760 364.760,537.000 362.000,537.000 L 322.000,537.000 C 319.240,537.000 317.000,534.760 317.000,532.000 L 317.000,492.000 C 317.000,489.240 319.240,487.000 322.000,487.000 Z"/>
<Path StrokeThickness="1.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Fill="#ff000000" Data=" M 492.000,487.000 L 532.000,487.000 C 534.760,487.000 537.000,489.240 537.000,492.000 L 537.000,532.000 C 537.000,534.760 534.760,537.000 532.000,537.000 L 492.000,537.000 C 489.240,537.000 487.000,534.760 487.000,532.000 L 487.000,492.000 C 487.000,489.240 489.240,487.000 492.000,487.000 Z"/>
<Path StrokeThickness="1.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Fill="#ff000000" Data=" M 747.000,487.000 L 787.000,487.000 C 789.760,487.000 792.000,489.240 792.000,492.000 L 792.000,532.000 C 792.000,534.760 789.760,537.000 787.000,537.000 L 747.000,537.000 C 744.240,537.000 742.000,534.760 742.000,532.000 L 742.000,492.000 C 742.000,489.240 744.240,487.000 747.000,487.000 Z"/>
<Path StrokeThickness="1.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Fill="#ff000000" Data=" M 152.000,392.000 L 192.000,392.000 C 194.760,392.000 197.000,394.240 197.000,397.000 L 197.000,437.000 C 197.000,439.760 194.760,442.000 192.000,442.000 L 152.000,442.000 C 149.240,442.000 147.000,439.760 147.000,437.000 L 147.000,397.000 C 147.000,394.240 149.240,392.000 152.000,392.000 Z"/>
<Path StrokeThickness="1.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Fill="#ff000000" Data=" M 322.000,392.000 L 362.000,392.000 C 364.760,392.000 367.000,394.240 367.000,397.000 L 367.000,437.000 C 367.000,439.760 364.760,442.000 362.000,442.000 L 322.000,442.000 C 319.240,442.000 317.000,439.760 317.000,437.000 L 317.000,397.000 C 317.000,394.240 319.240,392.000 322.000,392.000 Z"/>
<Path StrokeThickness="1.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Fill="#ff000000" Data=" M 492.000,392.000 L 532.000,392.000 C 534.760,392.000 537.000,394.240 537.000,397.000 L 537.000,437.000 C 537.000,439.760 534.760,442.000 532.000,442.000 L 492.000,442.000 C 489.240,442.000 487.000,439.760 487.000,437.000 L 487.000,397.000 C 487.000,394.240 489.240,392.000 492.000,392.000 Z"/>
<Path StrokeThickness="1.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Fill="#ff000000" Data=" M 747.000,392.000 L 787.000,392.000 C 789.760,392.000 792.000,394.240 792.000,397.000 L 792.000,437.000 C 792.000,439.760 789.760,442.000 787.000,442.000 L 747.000,442.000 C 744.240,442.000 742.000,439.760 742.000,437.000 L 742.000,397.000 C 742.000,394.240 744.240,392.000 747.000,392.000 Z"/>
<Path StrokeThickness="1.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Fill="#ff000000" Data=" M 237.000,582.000 L 277.000,582.000 C 279.760,582.000 282.000,584.240 282.000,587.000 L 282.000,627.000 C 282.000,629.760 279.760,632.000 277.000,632.000 L 237.000,632.000 C 234.240,632.000 232.000,629.760 232.000,627.000 L 232.000,587.000 C 232.000,584.240 234.240,582.000 237.000,582.000 Z"/>
<Path StrokeThickness="1.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Fill="#ff000000" Data=" M 322.000,582.000 L 702.000,582.000 C 704.760,582.000 707.000,584.240 707.000,587.000 L 707.000,627.000 C 707.000,629.760 704.760,632.000 702.000,632.000 L 322.000,632.000 C 319.240,632.000 317.000,629.760 317.000,627.000 L 317.000,587.000 C 317.000,584.240 319.240,582.000 322.000,582.000 Z"/>
<Path StrokeThickness="1.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Fill="#ff000000" Data=" M 832.000,582.000 L 872.000,582.000 C 874.760,582.000 877.000,584.240 877.000,587.000 L 877.000,627.000 C 877.000,629.760 874.760,632.000 872.000,632.000 L 832.000,632.000 C 829.240,632.000 827.000,629.760 827.000,627.000 L 827.000,587.000 C 827.000,584.240 829.240,582.000 832.000,582.000 Z"/>
<Path StrokeThickness="1.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Fill="#ff000000" Data=" M 237.000,487.000 L 277.000,487.000 C 279.760,487.000 282.000,489.240 282.000,492.000 L 282.000,532.000 C 282.000,534.760 279.760,537.000 277.000,537.000 L 237.000,537.000 C 234.240,537.000 232.000,534.760 232.000,532.000 L 232.000,492.000 C 232.000,489.240 234.240,487.000 237.000,487.000 Z"/>
<Path StrokeThickness="1.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Fill="#ff000000" Data=" M 407.000,487.000 L 447.000,487.000 C 449.760,487.000 452.000,489.240 452.000,492.000 L 452.000,532.000 C 452.000,534.760 449.760,537.000 447.000,537.000 L 407.000,537.000 C 404.240,537.000 402.000,534.760 402.000,532.000 L 402.000,492.000 C 402.000,489.240 404.240,487.000 407.000,487.000 Z"/>
<Path StrokeThickness="1.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Fill="#ff000000" Data=" M 577.000,487.000 L 617.000,487.000 C 619.760,487.000 622.000,489.240 622.000,492.000 L 622.000,532.000 C 622.000,534.760 619.760,537.000 617.000,537.000 L 577.000,537.000 C 574.240,537.000 572.000,534.760 572.000,532.000 L 572.000,492.000 C 572.000,489.240 574.240,487.000 577.000,487.000 Z"/>
<Path StrokeThickness="1.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Fill="#ff000000" Data=" M 662.000,487.000 L 702.000,487.000 C 704.760,487.000 707.000,489.240 707.000,492.000 L 707.000,532.000 C 707.000,534.760 704.760,537.000 702.000,537.000 L 662.000,537.000 C 659.240,537.000 657.000,534.760 657.000,532.000 L 657.000,492.000 C 657.000,489.240 659.240,487.000 662.000,487.000 Z"/>
<Path StrokeThickness="1.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Fill="#ff000000" Data=" M 832.000,487.000 L 872.000,487.000 C 874.760,487.000 877.000,489.240 877.000,492.000 L 877.000,532.000 C 877.000,534.760 874.760,537.000 872.000,537.000 L 832.000,537.000 C 829.240,537.000 827.000,534.760 827.000,532.000 L 827.000,492.000 C 827.000,489.240 829.240,487.000 832.000,487.000 Z"/>
<Path StrokeThickness="1.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Fill="#ff000000" Data=" M 237.000,392.000 L 277.000,392.000 C 279.760,392.000 282.000,394.240 282.000,397.000 L 282.000,437.000 C 282.000,439.760 279.760,442.000 277.000,442.000 L 237.000,442.000 C 234.240,442.000 232.000,439.760 232.000,437.000 L 232.000,397.000 C 232.000,394.240 234.240,392.000 237.000,392.000 Z"/>
<Path StrokeThickness="1.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Fill="#ff000000" Data=" M 407.000,392.000 L 447.000,392.000 C 449.760,392.000 452.000,394.240 452.000,397.000 L 452.000,437.000 C 452.000,439.760 449.760,442.000 447.000,442.000 L 407.000,442.000 C 404.240,442.000 402.000,439.760 402.000,437.000 L 402.000,397.000 C 402.000,394.240 404.240,392.000 407.000,392.000 Z"/>
<Path StrokeThickness="1.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Fill="#ff000000" Data=" M 577.000,392.000 L 617.000,392.000 C 619.760,392.000 622.000,394.240 622.000,397.000 L 622.000,437.000 C 622.000,439.760 619.760,442.000 617.000,442.000 L 577.000,442.000 C 574.240,442.000 572.000,439.760 572.000,437.000 L 572.000,397.000 C 572.000,394.240 574.240,392.000 577.000,392.000 Z"/>
<Path StrokeThickness="1.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Fill="#ff000000" Data=" M 662.000,392.000 L 702.000,392.000 C 704.760,392.000 707.000,394.240 707.000,397.000 L 707.000,437.000 C 707.000,439.760 704.760,442.000 702.000,442.000 L 662.000,442.000 C 659.240,442.000 657.000,439.760 657.000,437.000 L 657.000,397.000 C 657.000,394.240 659.240,392.000 662.000,392.000 Z"/>
<Path StrokeThickness="1.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Fill="#ff000000" Data=" M 832.000,392.000 L 872.000,392.000 C 874.760,392.000 877.000,394.240 877.000,397.000 L 877.000,437.000 C 877.000,439.760 874.760,442.000 872.000,442.000 L 832.000,442.000 C 829.240,442.000 827.000,439.760 827.000,437.000 L 827.000,397.000 C 827.000,394.240 829.240,392.000 832.000,392.000 Z"/>
<Path StrokeThickness="35.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 668.000,201.000 L 512.000,90.000 L 668.000,201.000 Z M 356.000,201.000 L 512.000,90.000 L 356.000,201.000 Z"/>
</Canvas>
</Canvas>
</Viewbox>

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

View File

@@ -0,0 +1,7 @@
<Viewbox Stretch="Uniform"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:fa="http://schemas.fontawesome.io/icons/">
<Canvas Height="120" Width="120">
<Path Stroke="Black" StrokeLineJoin="Round" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeThickness="8" Data="M 20,30 L 100,30 M 20,60 L 100,60 M 20,90 L 100,90" />
</Canvas>
</Viewbox>

View File

@@ -0,0 +1,12 @@
<Viewbox
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Canvas Width="1024.000" Height="1024.000">
<Canvas>
<Path Data=" M 0.000,0.000 L 1024.000,0.000 L 1024.000,1024.000 L 0.000,1024.000 L 0.000,0.000 Z"/>
<Path StrokeThickness="70.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 753.838,835.919 L 268.162,510.424 L 753.838,188.341"/>
<Path StrokeThickness="70.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 766.763,835.919"/>
<Path StrokeThickness="70.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 766.763,188.341"/>
</Canvas>
</Canvas>
</Viewbox>

View File

@@ -0,0 +1,12 @@
<Viewbox
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Canvas Width="1024.000" Height="1024.000">
<Canvas>
<Path Data=" M 0.000,0.000 L 1024.000,0.000 L 1024.000,1024.000 L 0.000,1024.000 L 0.000,0.000 Z"/>
<Path StrokeThickness="70.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 268.162,835.919 L 753.838,510.424 L 268.162,188.341"/>
<Path StrokeThickness="70.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 766.763,835.919"/>
<Path StrokeThickness="70.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 766.763,188.341"/>
</Canvas>
</Canvas>
</Viewbox>

View File

@@ -0,0 +1,9 @@
<Viewbox
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Canvas Width="1024.000" Height="1024.000">
<Canvas>
<Path Fill="Green" Data="F1 M 511.000,587.189 C 600.504,588.389 671.387,512.101 671.387,411.785 C 671.387,317.474 600.504,239.984 511.000,239.984 C 421.496,239.984 350.614,317.474 351.215,411.785 C 351.816,512.101 421.496,585.987 511.000,587.189 Z M 511.000,665.880 C 342.805,665.880 223.266,745.773 170.404,856.302 C 155.987,843.687 148.178,823.263 148.178,796.232 L 148.178,227.369 C 148.178,175.709 175.810,148.677 225.669,148.677 L 796.333,148.677 C 845.590,148.677 873.823,175.709 873.823,227.369 L 873.823,796.232 C 873.823,823.263 866.014,843.086 851.597,856.302 C 799.336,745.173 683.401,665.880 511.000,665.880 Z M 224.467,941.000 L 797.535,941.000 C 893.045,941.000 940.500,893.545 940.500,799.835 L 940.500,223.765 C 940.500,130.057 893.045,82.000 797.535,82.000 L 224.467,82.000 C 129.557,82.000 81.501,129.455 81.501,223.765 L 81.501,799.835 C 81.501,893.545 129.557,941.000 224.467,941.000 Z"/>
</Canvas>
</Canvas>
</Viewbox>

View File

@@ -0,0 +1,9 @@
<Viewbox
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Canvas Width="1024.000" Height="1024.000">
<Canvas>
<Path Fill="Black" Data="F1 M 511.000,587.189 C 600.504,588.389 671.387,512.101 671.387,411.785 C 671.387,317.474 600.504,239.984 511.000,239.984 C 421.496,239.984 350.614,317.474 351.215,411.785 C 351.816,512.101 421.496,585.987 511.000,587.189 Z M 511.000,665.880 C 342.805,665.880 223.266,745.773 170.404,856.302 C 155.987,843.687 148.178,823.263 148.178,796.232 L 148.178,227.369 C 148.178,175.709 175.810,148.677 225.669,148.677 L 796.333,148.677 C 845.590,148.677 873.823,175.709 873.823,227.369 L 873.823,796.232 C 873.823,823.263 866.014,843.086 851.597,856.302 C 799.336,745.173 683.401,665.880 511.000,665.880 Z M 224.467,941.000 L 797.535,941.000 C 893.045,941.000 940.500,893.545 940.500,799.835 L 940.500,223.765 C 940.500,130.057 893.045,82.000 797.535,82.000 L 224.467,82.000 C 129.557,82.000 81.501,129.455 81.501,223.765 L 81.501,799.835 C 81.501,893.545 129.557,941.000 224.467,941.000 Z"/>
</Canvas>
</Canvas>
</Viewbox>

View File

@@ -0,0 +1,11 @@
<Viewbox
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Canvas Width="1024.000" Height="1024.000">
<Canvas>
<Path Data=" M 0.000,0.000 L 1024.000,0.000 L 1024.000,1024.000 L 0.000,1024.000 L 0.000,0.000 Z"/>
<Path StrokeThickness="70.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 862.000,557.000 C 862.000,750.300 705.300,907.000 512.000,907.000 C 318.700,907.000 162.000,750.300 162.000,557.000 C 162.000,363.700 318.700,207.000 512.000,207.000"/>
<Path Fill="#ff000000" Data="F1 M 471.000,67.000 L 712.500,207.000 L 471.000,345.000 L 471.000,67.000 Z"/>
</Canvas>
</Canvas>
</Viewbox>

Binary file not shown.

After

Width:  |  Height:  |  Size: 361 KiB

View File

@@ -0,0 +1,9 @@
<Viewbox
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Canvas Width="996.513" Height="879.512">
<Path Fill="Green" Data="F1 M 648.026,733.342 L 132.172,733.342 C 95.142,733.342 74.057,713.285 74.057,674.713 L 74.057,191.286 C 74.057,152.201 95.142,132.658 132.172,132.658 L 821.827,132.658 C 858.855,132.658 879.940,152.201 879.940,191.286 L 879.940,533.629 C 906.670,538.022 931.750,547.790 954.000,561.758 L 954.000,187.686 C 954.000,101.287 910.798,58.086 822.855,58.086 L 131.143,58.086 C 43.714,58.086 0.000,101.287 0.000,187.686 L 0.000,678.313 C 0.000,764.714 43.714,807.914 131.143,807.914 L 663.555,807.914 C 653.801,784.825 648.291,759.630 648.026,733.342 Z"/>
<Path Fill="Green" Data="F1 M 819.770,533.037 L 819.770,225.230 C 819.770,204.657 808.455,192.315 788.398,192.315 L 165.600,192.315 C 146.057,192.315 134.229,204.657 134.229,225.230 L 134.229,640.771 C 134.229,661.342 146.057,673.685 165.600,673.685 L 656.640,673.685 C 679.042,600.500 742.705,544.204 819.770,533.037 Z"/>
<Path Fill="Green" Data="F1 M 848.358,879.512 C 765.828,879.512 700.700,813.887 700.700,731.358 C 700.700,649.325 765.828,583.203 847.860,583.203 C 930.391,583.203 996.513,649.325 996.513,731.358 C 996.513,813.887 930.391,879.512 848.358,879.512 Z"/>
</Canvas>
</Viewbox>

View File

@@ -0,0 +1,7 @@
<Viewbox
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Canvas Width="996.513" Height="879.512">
<Path Fill="Black" Data="F1 M 132.172,733.342 C 95.142,733.342 74.057,713.285 74.057,674.713 L 74.057,191.286 C 74.057,152.201 95.142,132.658 132.172,132.658 L 821.827,132.658 C 858.855,132.658 879.940,152.201 879.940,191.286 L 879.940,674.713 C 879.940,713.285 858.855,733.342 821.827,733.342 L 132.172,733.342 Z M 131.143,807.913 L 822.855,807.913 C 910.798,807.913 954.000,764.714 954.000,678.313 L 954.000,187.686 C 954.000,101.287 910.798,58.087 822.855,58.087 L 131.143,58.087 C 43.714,58.087 0.000,101.287 0.000,187.686 L 0.000,678.313 C 0.000,764.714 43.714,807.913 131.143,807.913 Z M 165.600,673.685 L 788.398,673.685 C 808.455,673.685 819.770,661.342 819.770,640.771 L 819.770,225.230 C 819.770,204.657 808.455,192.315 788.398,192.315 L 165.600,192.315 C 146.057,192.315 134.229,204.657 134.229,225.230 L 134.229,640.771 C 134.229,661.342 146.057,673.685 165.600,673.685 Z"/>
</Canvas>
</Viewbox>

View File

@@ -0,0 +1,12 @@
<Viewbox
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Canvas Width="1024.000" Height="1024.000">
<Canvas>
<Path StrokeThickness="79.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 512.000,902.000 C 296.610,902.000 122.000,727.280 122.000,511.740 C 122.000,388.110 179.450,277.910 269.090,206.400"/>
<Path StrokeThickness="79.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 512.000,902.000 C 727.390,902.000 902.000,727.280 902.000,511.740 C 902.000,388.110 844.550,277.910 754.910,206.400"/>
<Path StrokeThickness="79.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 512.560,112.000 L 512.560,494.460"/>
<Path Data=" M 0.000,0.000 L 1024.000,0.000 L 1024.000,1024.000 L 0.000,1024.000 L 0.000,0.000 Z"/>
</Canvas>
</Canvas>
</Viewbox>

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

View File

@@ -0,0 +1,16 @@
<Viewbox
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Canvas Width="1024.000" Height="1024.000">
<Canvas>
<Path Data=" M 0.000,0.000 L 1024.000,0.000 L 1024.000,1024.000 L 0.000,1024.000 L 0.000,0.000 Z"/>
<Path StrokeThickness="1.0" Stroke="#ffaaaaaa" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Fill="#ffaaaaaa" Data=" M 467.000,811.000 C 467.000,786.150 487.150,766.000 512.000,766.000 C 536.850,766.000 557.000,786.150 557.000,811.000 C 557.000,835.850 536.850,856.000 512.000,856.000 C 487.150,856.000 467.000,835.850 467.000,811.000 Z"/>
<Path StrokeThickness="35.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 82.530,352.000 C 191.500,240.330 343.650,171.000 512.000,171.000 C 680.350,171.000 832.500,240.330 941.470,352.000"/>
<Path StrokeThickness="70.0" Stroke="#ffaaaaaa" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 82.530,352.000 C 191.500,240.330 343.650,171.000 512.000,171.000 C 680.350,171.000 832.500,240.330 941.470,352.000"/>
<Path StrokeThickness="35.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 346.620,660.000 C 384.600,610.220 444.550,578.100 512.000,578.100 C 579.900,578.100 640.200,610.650 678.140,661.000"/>
<Path StrokeThickness="70.0" Stroke="#ffaaaaaa" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 346.620,660.000 C 384.600,610.220 444.550,578.100 512.000,578.100 C 579.900,578.100 640.200,610.650 678.140,661.000"/>
<Path StrokeThickness="35.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 213.050,505.000 C 288.650,426.810 394.650,378.200 512.000,378.200 C 629.350,378.200 735.350,426.810 810.950,505.000"/>
<Path StrokeThickness="70.0" Stroke="#ffaaaaaa" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 213.050,505.000 C 288.650,426.810 394.650,378.200 512.000,378.200 C 629.350,378.200 735.350,426.810 810.950,505.000"/>
</Canvas>
</Canvas>
</Viewbox>

View File

@@ -0,0 +1,16 @@
<Viewbox
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Canvas Width="1024.000" Height="1024.000">
<Canvas>
<Path Data=" M 0.000,0.000 L 1024.000,0.000 L 1024.000,1024.000 L 0.000,1024.000 L 0.000,0.000 Z"/>
<Path StrokeThickness="1.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Fill="#ff000000" Data=" M 467.000,811.000 C 467.000,786.150 487.150,766.000 512.000,766.000 C 536.850,766.000 557.000,786.150 557.000,811.000 C 557.000,835.850 536.850,856.000 512.000,856.000 C 487.150,856.000 467.000,835.850 467.000,811.000 Z"/>
<Path StrokeThickness="35.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 82.530,352.000 C 191.500,240.330 343.650,171.000 512.000,171.000 C 680.350,171.000 832.500,240.330 941.470,352.000"/>
<Path StrokeThickness="70.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 82.530,352.000 C 191.500,240.330 343.650,171.000 512.000,171.000 C 680.350,171.000 832.500,240.330 941.470,352.000"/>
<Path StrokeThickness="35.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 346.620,660.000 C 384.600,610.221 444.550,578.100 512.000,578.100 C 579.900,578.100 640.200,610.650 678.140,661.000"/>
<Path StrokeThickness="70.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 346.620,660.000 C 384.600,610.221 444.550,578.100 512.000,578.100 C 579.900,578.100 640.200,610.650 678.140,661.000"/>
<Path StrokeThickness="35.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 213.050,505.000 C 288.650,426.810 394.650,378.200 512.000,378.200 C 629.350,378.200 735.350,426.810 810.950,505.000"/>
<Path StrokeThickness="70.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 213.050,505.000 C 288.650,426.810 394.650,378.200 512.000,378.200 C 629.350,378.200 735.350,426.810 810.950,505.000"/>
</Canvas>
</Canvas>
</Viewbox>

View File

@@ -0,0 +1,16 @@
<Viewbox
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Canvas Width="1024.000" Height="1024.000">
<Canvas>
<Path Data=" M 0.000,0.000 L 1024.000,0.000 L 1024.000,1024.000 L 0.000,1024.000 L 0.000,0.000 Z"/>
<Path StrokeThickness="1.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Fill="#ff000000" Data=" M 467.000,811.000 C 467.000,786.150 487.150,766.000 512.000,766.000 C 536.850,766.000 557.000,786.150 557.000,811.000 C 557.000,835.850 536.850,856.000 512.000,856.000 C 487.150,856.000 467.000,835.850 467.000,811.000 Z"/>
<Path StrokeThickness="35.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 82.530,352.000 C 191.500,240.330 343.650,171.000 512.000,171.000 C 680.350,171.000 832.500,240.330 941.470,352.000"/>
<Path StrokeThickness="70.0" Stroke="#ffaaaaaa" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 82.530,352.000 C 191.500,240.330 343.650,171.000 512.000,171.000 C 680.350,171.000 832.500,240.330 941.470,352.000"/>
<Path StrokeThickness="35.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 346.620,660.000 C 384.600,610.220 444.550,578.100 512.000,578.100 C 579.900,578.100 640.200,610.650 678.140,661.000"/>
<Path StrokeThickness="70.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 346.620,660.000 C 384.600,610.220 444.550,578.100 512.000,578.100 C 579.900,578.100 640.200,610.650 678.140,661.000"/>
<Path StrokeThickness="35.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 213.050,505.000 C 288.650,426.810 394.650,378.200 512.000,378.200 C 629.350,378.200 735.350,426.810 810.950,505.000"/>
<Path StrokeThickness="70.0" Stroke="#ffaaaaaa" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 213.050,505.000 C 288.650,426.810 394.650,378.200 512.000,378.200 C 629.350,378.200 735.350,426.810 810.950,505.000"/>
</Canvas>
</Canvas>
</Viewbox>

View File

@@ -0,0 +1,16 @@
<Viewbox
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Canvas Width="1024.000" Height="1024.000">
<Canvas>
<Path Data=" M 0.000,0.000 L 1024.000,0.000 L 1024.000,1024.000 L 0.000,1024.000 L 0.000,0.000 Z"/>
<Path StrokeThickness="1.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Fill="#ff000000" Data=" M 467.000,811.000 C 467.000,786.150 487.150,766.000 512.000,766.000 C 536.850,766.000 557.000,786.150 557.000,811.000 C 557.000,835.850 536.850,856.000 512.000,856.000 C 487.150,856.000 467.000,835.850 467.000,811.000 Z"/>
<Path StrokeThickness="35.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 82.530,352.000 C 191.500,240.330 343.650,171.000 512.000,171.000 C 680.350,171.000 832.500,240.330 941.470,352.000"/>
<Path StrokeThickness="70.0" Stroke="#ffaaaaaa" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 82.530,352.000 C 191.500,240.330 343.650,171.000 512.000,171.000 C 680.350,171.000 832.500,240.330 941.470,352.000"/>
<Path StrokeThickness="35.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 346.620,660.000 C 384.600,610.220 444.550,578.100 512.000,578.100 C 579.900,578.100 640.200,610.650 678.140,661.000"/>
<Path StrokeThickness="70.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 346.620,660.000 C 384.600,610.220 444.550,578.100 512.000,578.100 C 579.900,578.100 640.200,610.650 678.140,661.000"/>
<Path StrokeThickness="35.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 213.050,505.000 C 288.650,426.810 394.650,378.200 512.000,378.200 C 629.350,378.200 735.350,426.810 810.950,505.000"/>
<Path StrokeThickness="70.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 213.050,505.000 C 288.650,426.810 394.650,378.200 512.000,378.200 C 629.350,378.200 735.350,426.810 810.950,505.000"/>
</Canvas>
</Canvas>
</Viewbox>

View File

@@ -0,0 +1,16 @@
<Viewbox
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Canvas Width="1024.000" Height="1024.000">
<Canvas>
<Path Data=" M 0.000,0.000 L 1024.000,0.000 L 1024.000,1024.000 L 0.000,1024.000 L 0.000,0.000 Z"/>
<Path StrokeThickness="1.0" Stroke="#ffdddddd" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Fill="#ffdddddd" Data=" M 467.000,811.000 C 467.000,786.150 487.150,766.000 512.000,766.000 C 536.850,766.000 557.000,786.150 557.000,811.000 C 557.000,835.850 536.850,856.000 512.000,856.000 C 487.150,856.000 467.000,835.850 467.000,811.000 Z"/>
<Path StrokeThickness="35.0" Stroke="#ffdddddd" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 82.530,352.000 C 191.500,240.330 343.650,171.000 512.000,171.000 C 680.350,171.000 832.500,240.330 941.470,352.000"/>
<Path StrokeThickness="70.0" Stroke="#ffdddddd" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 82.530,352.000 C 191.500,240.330 343.650,171.000 512.000,171.000 C 680.350,171.000 832.500,240.330 941.470,352.000"/>
<Path StrokeThickness="35.0" Stroke="#ffdddddd" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 346.620,660.000 C 384.600,610.220 444.550,578.100 512.000,578.100 C 579.900,578.100 640.200,610.650 678.140,661.000"/>
<Path StrokeThickness="70.0" Stroke="#ffdddddd" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 346.620,660.000 C 384.600,610.220 444.550,578.100 512.000,578.100 C 579.900,578.100 640.200,610.650 678.140,661.000"/>
<Path StrokeThickness="35.0" Stroke="#ffdddddd" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 213.050,505.000 C 288.650,426.810 394.650,378.200 512.000,378.200 C 629.350,378.200 735.350,426.810 810.950,505.000"/>
<Path StrokeThickness="70.0" Stroke="#ffdddddd" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 213.050,505.000 C 288.650,426.810 394.650,378.200 512.000,378.200 C 629.350,378.200 735.350,426.810 810.950,505.000"/>
</Canvas>
</Canvas>
</Viewbox>

View File

@@ -0,0 +1,16 @@
<Viewbox
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Canvas Width="1024.000" Height="1024.000">
<Canvas>
<Path Data=" M 0.000,0.000 L 1024.000,0.000 L 1024.000,1024.000 L 0.000,1024.000 L 0.000,0.000 Z"/>
<Path StrokeThickness="1.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Fill="#ff000000" Data=" M 467.000,811.000 C 467.000,786.150 487.150,766.000 512.000,766.000 C 536.850,766.000 557.000,786.150 557.000,811.000 C 557.000,835.850 536.850,856.000 512.000,856.000 C 487.150,856.000 467.000,835.850 467.000,811.000 Z"/>
<Path StrokeThickness="35.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 82.530,352.000 C 191.500,240.330 343.650,171.000 512.000,171.000 C 680.350,171.000 832.500,240.330 941.470,352.000"/>
<Path StrokeThickness="70.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 82.530,352.000 C 191.500,240.330 343.650,171.000 512.000,171.000 C 680.350,171.000 832.500,240.330 941.470,352.000"/>
<Path StrokeThickness="35.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 346.620,660.000 C 384.600,610.221 444.550,578.100 512.000,578.100 C 579.900,578.100 640.200,610.650 678.140,661.000"/>
<Path StrokeThickness="70.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 346.620,660.000 C 384.600,610.221 444.550,578.100 512.000,578.100 C 579.900,578.100 640.200,610.650 678.140,661.000"/>
<Path StrokeThickness="35.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 213.050,505.000 C 288.650,426.810 394.650,378.200 512.000,378.200 C 629.350,378.200 735.350,426.810 810.950,505.000"/>
<Path StrokeThickness="70.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 213.050,505.000 C 288.650,426.810 394.650,378.200 512.000,378.200 C 629.350,378.200 735.350,426.810 810.950,505.000"/>
</Canvas>
</Canvas>
</Viewbox>

View File

@@ -0,0 +1,16 @@
<Viewbox
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Canvas Width="1024.000" Height="1024.000">
<Canvas>
<Path Data=" M 0.000,0.000 L 1024.000,0.000 L 1024.000,1024.000 L 0.000,1024.000 L 0.000,0.000 Z"/>
<Path StrokeThickness="1.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Fill="#ff000000" Data=" M 467.000,811.000 C 467.000,786.150 487.150,766.000 512.000,766.000 C 536.850,766.000 557.000,786.150 557.000,811.000 C 557.000,835.850 536.850,856.000 512.000,856.000 C 487.150,856.000 467.000,835.850 467.000,811.000 Z"/>
<Path StrokeThickness="35.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 82.530,352.000 C 191.500,240.330 343.650,171.000 512.000,171.000 C 680.350,171.000 832.500,240.330 941.470,352.000"/>
<Path StrokeThickness="70.0" Stroke="#ffdddddd" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 82.530,352.000 C 191.500,240.330 343.650,171.000 512.000,171.000 C 680.350,171.000 832.500,240.330 941.470,352.000"/>
<Path StrokeThickness="35.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 346.620,660.000 C 384.600,610.220 444.550,578.100 512.000,578.100 C 579.900,578.100 640.200,610.650 678.140,661.000"/>
<Path StrokeThickness="70.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 346.620,660.000 C 384.600,610.220 444.550,578.100 512.000,578.100 C 579.900,578.100 640.200,610.650 678.140,661.000"/>
<Path StrokeThickness="35.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 213.050,505.000 C 288.650,426.810 394.650,378.200 512.000,378.200 C 629.350,378.200 735.350,426.810 810.950,505.000"/>
<Path StrokeThickness="70.0" Stroke="#ffdddddd" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 213.050,505.000 C 288.650,426.810 394.650,378.200 512.000,378.200 C 629.350,378.200 735.350,426.810 810.950,505.000"/>
</Canvas>
</Canvas>
</Viewbox>

View File

@@ -0,0 +1,16 @@
<Viewbox
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Canvas Width="1024.000" Height="1024.000">
<Canvas>
<Path Data=" M 0.000,0.000 L 1024.000,0.000 L 1024.000,1024.000 L 0.000,1024.000 L 0.000,0.000 Z"/>
<Path StrokeThickness="1.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Fill="#ff000000" Data=" M 467.000,811.000 C 467.000,786.150 487.150,766.000 512.000,766.000 C 536.850,766.000 557.000,786.150 557.000,811.000 C 557.000,835.850 536.850,856.000 512.000,856.000 C 487.150,856.000 467.000,835.850 467.000,811.000 Z"/>
<Path StrokeThickness="35.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 82.530,352.000 C 191.500,240.330 343.650,171.000 512.000,171.000 C 680.350,171.000 832.500,240.330 941.470,352.000"/>
<Path StrokeThickness="70.0" Stroke="#ffdddddd" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 82.530,352.000 C 191.500,240.330 343.650,171.000 512.000,171.000 C 680.350,171.000 832.500,240.330 941.470,352.000"/>
<Path StrokeThickness="35.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 346.620,660.000 C 384.600,610.220 444.550,578.100 512.000,578.100 C 579.900,578.100 640.200,610.650 678.140,661.000"/>
<Path StrokeThickness="70.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 346.620,660.000 C 384.600,610.220 444.550,578.100 512.000,578.100 C 579.900,578.100 640.200,610.650 678.140,661.000"/>
<Path StrokeThickness="35.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 213.050,505.000 C 288.650,426.810 394.650,378.200 512.000,378.200 C 629.350,378.200 735.350,426.810 810.950,505.000"/>
<Path StrokeThickness="70.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 213.050,505.000 C 288.650,426.810 394.650,378.200 512.000,378.200 C 629.350,378.200 735.350,426.810 810.950,505.000"/>
</Canvas>
</Canvas>
</Viewbox>

View File

@@ -0,0 +1,12 @@
<Viewbox
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Canvas Width="40.000" Height="40.000">
<Canvas>
<Path Data="F1 M 32.000,36.000 L 9.000,36.000 L 9.000,5.000 L 32.000,5.000 L 32.000,36.000 Z"/>
</Canvas>
<Canvas>
<Path StrokeThickness="1.9" Stroke="#ff666666" StrokeLineJoin="Round" Data="F1 M 31.258,13.484 L 23.773,6.000 M 31.258,35.000 L 9.742,35.000 L 9.742,6.001 L 23.773,6.001 L 23.773,13.485 L 31.258,13.484 L 31.258,35.000 Z"/>
</Canvas>
</Canvas>
</Viewbox>

View File

@@ -0,0 +1,12 @@
<Viewbox
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Canvas Width="40.000" Height="40.000">
<Canvas>
<Path Data="F1 M 32.000,36.000 L 9.000,36.000 L 9.000,5.000 L 32.000,5.000 L 32.000,36.000 Z"/>
</Canvas>
<Canvas>
<Path StrokeThickness="2.0" Stroke="#ff666666" StrokeLineJoin="Round" Data="F1 M 27.000,14.870 L 22.129,10.000 M 27.000,28.869 L 13.000,28.869 L 13.000,10.000 L 22.129,10.000 L 22.129,14.870 L 27.000,14.870 L 27.000,28.869 Z"/>
</Canvas>
</Canvas>
</Viewbox>

View File

@@ -0,0 +1,48 @@
/*
* Copyright (c) 2024 ETH Zürich, IT Services
*
* 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.MessageBox;
using SafeExamBrowser.UserInterface.Contracts.Windows;
using SafeExamBrowser.UserInterface.Mobile.Windows;
using MessageBoxResult = SafeExamBrowser.UserInterface.Contracts.MessageBox.MessageBoxResult;
namespace SafeExamBrowser.UserInterface.Mobile
{
public class MessageBoxFactory : IMessageBox
{
private IText text;
public MessageBoxFactory(IText text)
{
this.text = text;
}
public MessageBoxResult Show(string message, string title, MessageBoxAction action = MessageBoxAction.Ok, MessageBoxIcon icon = MessageBoxIcon.Information, IWindow parent = null)
{
var result = default(MessageBoxResult);
if (parent is Window window)
{
result = window.Dispatcher.Invoke(() => new MessageBoxDialog(text).Show(message, title, action, icon, window));
}
else
{
result = new MessageBoxDialog(text).Show(message, title, action, icon);
}
return result;
}
public MessageBoxResult Show(TextKey message, TextKey title, MessageBoxAction action = MessageBoxAction.Ok, MessageBoxIcon icon = MessageBoxIcon.Information, IWindow parent = null)
{
return Show(text.Get(message), text.Get(title), action, icon, parent);
}
}
}

View File

@@ -0,0 +1,51 @@
using System.Reflection;
using System.Runtime.InteropServices;
using System.Windows;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("SafeExamBrowser.UserInterface.Mobile")]
[assembly: AssemblyDescription("Safe Exam Browser")]
[assembly: AssemblyCompany("ETH Zürich")]
[assembly: AssemblyProduct("SafeExamBrowser.UserInterface.Mobile")]
[assembly: AssemblyCopyright("Copyright © 2024 ETH Zürich, IT Services")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
//In order to begin building localizable applications, set
//<UICulture>CultureYouAreCodingWith</UICulture> in your .csproj file
//inside a <PropertyGroup>. For example, if you are using US english
//in your source files, set the <UICulture> to en-US. Then uncomment
//the NeutralResourceLanguage attribute below. Update the "en-US" in
//the line below to match the UICulture setting in the project file.
//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
[assembly: ThemeInfo(
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
//(used if a resource is not found in the page,
// or application resource dictionaries)
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
//(used if a resource is not found in the page,
// app, or any theme specific resource dictionaries)
)]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0.0")]

View File

@@ -0,0 +1,597 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{89BC24DD-FF31-496E-9816-A160B686A3D4}</ProjectGuid>
<OutputType>library</OutputType>
<RootNamespace>SafeExamBrowser.UserInterface.Mobile</RootNamespace>
<AssemblyName>SafeExamBrowser.UserInterface.Mobile</AssemblyName>
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<WarningLevel>4</WarningLevel>
<Deterministic>true</Deterministic>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x86\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
<OutputPath>bin\x86\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<OutputPath>bin\x64\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
<Reference Include="FontAwesome.WPF, Version=4.7.0.37774, Culture=neutral, PublicKeyToken=0758b07a11a4f466, processorArchitecture=MSIL">
<HintPath>..\packages\FontAwesome.WPF.4.7.0.9\lib\net40\FontAwesome.WPF.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Drawing" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Xaml">
<RequiredTargetFramework>4.0</RequiredTargetFramework>
</Reference>
<Reference Include="WindowsBase" />
<Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" />
<Reference Include="WindowsFormsIntegration" />
</ItemGroup>
<ItemGroup>
<Compile Include="Controls\ActionCenter\RaiseHandControl.xaml.cs">
<DependentUpon>RaiseHandControl.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\Taskbar\RaiseHandControl.xaml.cs">
<DependentUpon>RaiseHandControl.xaml</DependentUpon>
</Compile>
<Compile Include="Windows\AboutWindow.xaml.cs">
<DependentUpon>AboutWindow.xaml</DependentUpon>
</Compile>
<Compile Include="Windows\ActionCenter.xaml.cs">
<DependentUpon>ActionCenter.xaml</DependentUpon>
</Compile>
<Compile Include="Windows\BrowserWindow.xaml.cs">
<DependentUpon>BrowserWindow.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\ActionCenter\ApplicationButton.xaml.cs">
<DependentUpon>ApplicationButton.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\ActionCenter\ApplicationControl.xaml.cs">
<DependentUpon>ApplicationControl.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\ActionCenter\AudioControl.xaml.cs">
<DependentUpon>AudioControl.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\ActionCenter\Clock.xaml.cs">
<DependentUpon>Clock.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\ActionCenter\KeyboardLayoutButton.xaml.cs">
<DependentUpon>KeyboardLayoutButton.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\ActionCenter\KeyboardLayoutControl.xaml.cs">
<DependentUpon>KeyboardLayoutControl.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\ActionCenter\NotificationButton.xaml.cs">
<DependentUpon>NotificationButton.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\ActionCenter\PowerSupplyControl.xaml.cs">
<DependentUpon>PowerSupplyControl.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\ActionCenter\QuitButton.xaml.cs">
<DependentUpon>QuitButton.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\ActionCenter\NetworkButton.xaml.cs">
<DependentUpon>NetworkButton.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\ActionCenter\NetworkControl.xaml.cs">
<DependentUpon>NetworkControl.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\Browser\DownloadItemControl.xaml.cs">
<DependentUpon>DownloadItemControl.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\Taskbar\ApplicationControl.xaml.cs">
<DependentUpon>ApplicationControl.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\Taskbar\ApplicationWindowButton.xaml.cs">
<DependentUpon>ApplicationWindowButton.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\Taskbar\AudioControl.xaml.cs">
<DependentUpon>AudioControl.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\Taskbar\Clock.xaml.cs">
<DependentUpon>Clock.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\Taskbar\KeyboardLayoutButton.xaml.cs">
<DependentUpon>KeyboardLayoutButton.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\Taskbar\KeyboardLayoutControl.xaml.cs">
<DependentUpon>KeyboardLayoutControl.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\Taskbar\NotificationButton.xaml.cs">
<DependentUpon>NotificationButton.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\Taskbar\PowerSupplyControl.xaml.cs">
<DependentUpon>PowerSupplyControl.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\Taskbar\QuitButton.xaml.cs">
<DependentUpon>QuitButton.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\Taskbar\NetworkButton.xaml.cs">
<DependentUpon>NetworkButton.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\Taskbar\NetworkControl.xaml.cs">
<DependentUpon>NetworkControl.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\Taskview\WindowControl.xaml.cs">
<DependentUpon>WindowControl.xaml</DependentUpon>
</Compile>
<Compile Include="Windows\ExamSelectionDialog.xaml.cs">
<DependentUpon>ExamSelectionDialog.xaml</DependentUpon>
</Compile>
<Compile Include="Windows\FileSystemDialog.xaml.cs">
<DependentUpon>FileSystemDialog.xaml</DependentUpon>
</Compile>
<Compile Include="FileSystemDialogFactory.cs" />
<Compile Include="Windows\LockScreen.xaml.cs">
<DependentUpon>LockScreen.xaml</DependentUpon>
</Compile>
<Compile Include="Windows\LogWindow.xaml.cs">
<DependentUpon>LogWindow.xaml</DependentUpon>
</Compile>
<Compile Include="MessageBoxFactory.cs" />
<Compile Include="Windows\MessageBoxDialog.xaml.cs">
<DependentUpon>MessageBoxDialog.xaml</DependentUpon>
</Compile>
<Compile Include="Windows\CredentialsDialog.xaml.cs">
<DependentUpon>CredentialsDialog.xaml</DependentUpon>
</Compile>
<Compile Include="Windows\PasswordDialog.xaml.cs">
<DependentUpon>PasswordDialog.xaml</DependentUpon>
</Compile>
<Compile Include="Properties\AssemblyInfo.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Windows\ProctoringFinalizationDialog.xaml.cs">
<DependentUpon>ProctoringFinalizationDialog.xaml</DependentUpon>
</Compile>
<Compile Include="Windows\ProctoringWindow.xaml.cs">
<DependentUpon>ProctoringWindow.xaml</DependentUpon>
</Compile>
<Compile Include="Windows\RuntimeWindow.xaml.cs">
<DependentUpon>RuntimeWindow.xaml</DependentUpon>
</Compile>
<Compile Include="Windows\ServerFailureDialog.xaml.cs">
<DependentUpon>ServerFailureDialog.xaml</DependentUpon>
</Compile>
<Compile Include="Windows\SplashScreen.xaml.cs">
<DependentUpon>SplashScreen.xaml</DependentUpon>
</Compile>
<Compile Include="Windows\Taskbar.xaml.cs">
<DependentUpon>Taskbar.xaml</DependentUpon>
</Compile>
<Compile Include="Windows\Taskview.xaml.cs">
<DependentUpon>Taskview.xaml</DependentUpon>
</Compile>
<Compile Include="UserInterfaceFactory.cs" />
<Compile Include="ViewModels\DateTimeViewModel.cs" />
<Compile Include="ViewModels\LogViewModel.cs" />
<Compile Include="ViewModels\ProgressIndicatorViewModel.cs" />
<Compile Include="ViewModels\RuntimeWindowViewModel.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\SafeExamBrowser.Applications.Contracts\SafeExamBrowser.Applications.Contracts.csproj">
<Project>{ac77745d-3b41-43e2-8e84-d40e5a4ee77f}</Project>
<Name>SafeExamBrowser.Applications.Contracts</Name>
</ProjectReference>
<ProjectReference Include="..\SafeExamBrowser.Browser.Contracts\SafeExamBrowser.Browser.Contracts.csproj">
<Project>{5fb5273d-277c-41dd-8593-a25ce1aff2e9}</Project>
<Name>SafeExamBrowser.Browser.Contracts</Name>
</ProjectReference>
<ProjectReference Include="..\SafeExamBrowser.Configuration.Contracts\SafeExamBrowser.Configuration.Contracts.csproj">
<Project>{7d74555e-63e1-4c46-bd0a-8580552368c8}</Project>
<Name>SafeExamBrowser.Configuration.Contracts</Name>
</ProjectReference>
<ProjectReference Include="..\SafeExamBrowser.Core.Contracts\SafeExamBrowser.Core.Contracts.csproj">
<Project>{fe0e1224-b447-4b14-81e7-ed7d84822aa0}</Project>
<Name>SafeExamBrowser.Core.Contracts</Name>
</ProjectReference>
<ProjectReference Include="..\SafeExamBrowser.I18n.Contracts\SafeExamBrowser.I18n.Contracts.csproj">
<Project>{1858ddf3-bc2a-4bff-b663-4ce2ffeb8b7d}</Project>
<Name>SafeExamBrowser.I18n.Contracts</Name>
</ProjectReference>
<ProjectReference Include="..\SafeExamBrowser.Logging.Contracts\SafeExamBrowser.Logging.Contracts.csproj">
<Project>{64ea30fb-11d4-436a-9c2b-88566285363e}</Project>
<Name>SafeExamBrowser.Logging.Contracts</Name>
</ProjectReference>
<ProjectReference Include="..\SafeExamBrowser.Proctoring.Contracts\SafeExamBrowser.Proctoring.Contracts.csproj">
<Project>{8E52BD1C-0540-4F16-B181-6665D43F7A7B}</Project>
<Name>SafeExamBrowser.Proctoring.Contracts</Name>
</ProjectReference>
<ProjectReference Include="..\SafeExamBrowser.Server.Contracts\SafeExamBrowser.Server.Contracts.csproj">
<Project>{DB701E6F-BDDC-4CEC-B662-335A9DC11809}</Project>
<Name>SafeExamBrowser.Server.Contracts</Name>
</ProjectReference>
<ProjectReference Include="..\SafeExamBrowser.Settings\SafeExamBrowser.Settings.csproj">
<Project>{30b2d907-5861-4f39-abad-c4abf1b3470e}</Project>
<Name>SafeExamBrowser.Settings</Name>
</ProjectReference>
<ProjectReference Include="..\SafeExamBrowser.SystemComponents.Contracts\SafeExamBrowser.SystemComponents.Contracts.csproj">
<Project>{903129c6-e236-493b-9ad6-c6a57f647a3a}</Project>
<Name>SafeExamBrowser.SystemComponents.Contracts</Name>
</ProjectReference>
<ProjectReference Include="..\SafeExamBrowser.UserInterface.Contracts\SafeExamBrowser.UserInterface.Contracts.csproj">
<Project>{c7889e97-6ff6-4a58-b7cb-521ed276b316}</Project>
<Name>SafeExamBrowser.UserInterface.Contracts</Name>
</ProjectReference>
<ProjectReference Include="..\SafeExamBrowser.UserInterface.Shared\SafeExamBrowser.UserInterface.Shared.csproj">
<Project>{38525928-87ba-4f8c-8010-4eb97bfaae13}</Project>
<Name>SafeExamBrowser.UserInterface.Shared</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Resource Include="Images\Proctoring_Inactive.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Resource>
<Resource Include="Images\Proctoring_Active.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Resource>
<Page Include="Controls\ActionCenter\RaiseHandControl.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Controls\Taskbar\RaiseHandControl.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Resource Include="Images\Hand_Lowered.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Resource>
<Resource Include="Images\Hand_Raised.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Resource>
<Resource Include="Images\ScreenProctoring_Active.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Resource>
<Resource Include="Images\ScreenProctoring_Inactive.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Resource>
<Page Include="Windows\AboutWindow.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Windows\ActionCenter.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Windows\BrowserWindow.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Controls\ActionCenter\ApplicationButton.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Controls\ActionCenter\ApplicationControl.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Controls\ActionCenter\AudioControl.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Controls\ActionCenter\Clock.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Controls\ActionCenter\KeyboardLayoutButton.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Controls\ActionCenter\KeyboardLayoutControl.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Controls\ActionCenter\NotificationButton.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Controls\ActionCenter\PowerSupplyControl.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Controls\ActionCenter\QuitButton.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Controls\ActionCenter\NetworkButton.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Controls\ActionCenter\NetworkControl.xaml">
<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\Taskbar\ApplicationControl.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Controls\Taskbar\ApplicationWindowButton.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Controls\Taskbar\AudioControl.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Controls\Taskbar\Clock.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Controls\Taskbar\KeyboardLayoutButton.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Controls\Taskbar\KeyboardLayoutControl.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Controls\Taskbar\NotificationButton.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Controls\Taskbar\PowerSupplyControl.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Controls\Taskbar\QuitButton.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Controls\Taskbar\NetworkButton.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Controls\Taskbar\NetworkControl.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Resource Include="Images\Audio_100.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Resource>
<Resource Include="Images\Audio_33.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Resource>
<Resource Include="Images\Audio_66.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Resource>
<Resource Include="Images\Audio_Light_100.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Resource>
<Resource Include="Images\Audio_Light_33.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Resource>
<Resource Include="Images\Audio_Light_66.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Resource>
<Resource Include="Images\Audio_Light_NoDevice.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Resource>
<Resource Include="Images\Audio_Muted.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Resource>
<Resource Include="Images\Audio_NoDevice.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Resource>
<Page Include="Controls\Taskview\WindowControl.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Windows\ExamSelectionDialog.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Windows\FileSystemDialog.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Images\ZoomPageOut.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Images\ZoomPageIn.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Resource Include="Images\WiFi_Light_66.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Resource>
<Resource Include="Images\WiFi_Light_33.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Resource>
<Resource Include="Images\WiFi_Light_100.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Resource>
<Resource Include="Images\WiFi_Light_0.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Resource>
<Resource Include="Images\WiFi_66.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Resource>
<Resource Include="Images\WiFi_33.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Resource>
<Resource Include="Images\WiFi_100.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Resource>
<Resource Include="Images\WiFi_0.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Resource>
<Resource Include="Images\Home.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Resource>
<Page Include="Images\ShutDown.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Resource Include="Images\Reload.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Resource>
<Resource Include="Images\NavigateForward.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Resource>
<Resource Include="Images\NavigateBack.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Resource>
<Resource Include="Images\Menu.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Resource>
<Page Include="Images\Keyboard.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Images\Battery.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Images\AboutNotification.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Windows\LockScreen.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Windows\LogWindow.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Windows\MessageBoxDialog.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Windows\CredentialsDialog.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Windows\PasswordDialog.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Windows\ProctoringFinalizationDialog.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Windows\ProctoringWindow.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Windows\RuntimeWindow.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Windows\ServerFailureDialog.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Windows\SplashScreen.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Windows\Taskbar.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Windows\Taskview.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Templates\Buttons.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Templates\Colors.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Templates\ScrollViewers.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
<ItemGroup>
<Resource Include="Images\LogNotification.ico" />
</ItemGroup>
<ItemGroup>
<Resource Include="Images\SafeExamBrowser.ico" />
</ItemGroup>
<ItemGroup>
<Resource Include="Images\SplashScreen.png" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View File

@@ -0,0 +1,61 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="../Templates/Colors.xaml" />
</ResourceDictionary.MergedDictionaries>
<ControlTemplate x:Key="ActionCenterButton" TargetType="Button">
<Border x:Name="ButtonContent" Background="Transparent" BorderBrush="Transparent" BorderThickness="1" Cursor="Hand" Padding="{TemplateBinding Padding}">
<ContentPresenter ContentSource="Content" HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
RenderOptions.BitmapScalingMode="HighQuality" VerticalAlignment="{TemplateBinding VerticalAlignment}" />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="ButtonContent" Property="Background" Value="#AAADD8E6" />
<Setter TargetName="ButtonContent" Property="BorderBrush" Value="DodgerBlue" />
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter TargetName="ButtonContent" Property="BorderBrush" Value="#AA87CEEB" />
<Setter TargetName="ButtonContent" Property="BorderThickness" Value="2" />
<Setter Property="Cursor" Value="Hand" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
<ControlTemplate x:Key="BrowserButton" TargetType="Button">
<Border x:Name="ButtonContent" Background="Transparent" BorderBrush="Transparent" BorderThickness="1" Cursor="Hand" Padding="{TemplateBinding Padding}">
<ContentPresenter ContentSource="Content" HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
RenderOptions.BitmapScalingMode="HighQuality" VerticalAlignment="{TemplateBinding VerticalAlignment}" />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="ButtonContent" Property="Opacity" Value="0.25" />
</Trigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="ButtonContent" Property="Background" Value="LightBlue" />
<Setter TargetName="ButtonContent" Property="BorderBrush" Value="DodgerBlue" />
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter TargetName="ButtonContent" Property="BorderBrush" Value="SkyBlue" />
<Setter TargetName="ButtonContent" Property="BorderThickness" Value="2" />
<Setter Property="Cursor" Value="Hand" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
<ControlTemplate x:Key="TaskbarButton" TargetType="Button">
<Border x:Name="ButtonContent" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding Background}"
BorderThickness="1" Cursor="Hand" Padding="{TemplateBinding Padding}">
<ContentPresenter ContentSource="Content" HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
RenderOptions.BitmapScalingMode="HighQuality" VerticalAlignment="{TemplateBinding VerticalAlignment}" />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="ButtonContent" Property="Background" Value="LightBlue" />
<Setter TargetName="ButtonContent" Property="BorderBrush" Value="DodgerBlue" />
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter TargetName="ButtonContent" Property="BorderBrush" Value="SkyBlue" />
<Setter TargetName="ButtonContent" Property="BorderThickness" Value="2" />
<Setter Property="Cursor" Value="Hand" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</ResourceDictionary>

View File

@@ -0,0 +1,9 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<SolidColorBrush x:Key="ActionCenterDarkBrush">#AA808080</SolidColorBrush>
<SolidColorBrush x:Key="BackgroundBrush">#FFF0F0F0</SolidColorBrush>
<SolidColorBrush x:Key="BackgroundTransparentBrush">#EEF0F0F0</SolidColorBrush>
<SolidColorBrush x:Key="BackgroundTransparentEmphasisBrush">#99D3D3D3</SolidColorBrush>
<SolidColorBrush x:Key="PrimaryTextBrush">Black</SolidColorBrush>
<SolidColorBrush x:Key="SecondaryTextBrush">DimGray</SolidColorBrush>
</ResourceDictionary>

View File

@@ -0,0 +1,59 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ControlTemplate x:Key="Thumb" TargetType="{x:Type Thumb}">
<Rectangle x:Name="Rectangle" Fill="DarkGray" Height="Auto" Width="Auto" />
<ControlTemplate.Triggers>
<Trigger Property="IsDragging" Value="True">
<Setter TargetName="Rectangle" Property="Fill" Value="DimGray" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
<ControlTemplate x:Key="RepeatButton" TargetType="RepeatButton">
<Rectangle Fill="DarkGray" Height="Auto" Width="Auto" />
</ControlTemplate>
<ControlTemplate x:Key="VerticalScrollBar" TargetType="{x:Type ScrollBar}">
<Track Name="PART_Track" Height="Auto" IsDirectionReversed="True" Width="10">
<Track.DecreaseRepeatButton>
<RepeatButton Margin="4.5,0" Template="{StaticResource RepeatButton}" />
</Track.DecreaseRepeatButton>
<Track.Thumb>
<Thumb Cursor="ScrollNS" Focusable="False" Margin="2.5,0" OverridesDefaultStyle="True" SnapsToDevicePixels="True" Template="{StaticResource Thumb}" />
</Track.Thumb>
<Track.IncreaseRepeatButton>
<RepeatButton Margin="4.5,0" Template="{StaticResource RepeatButton}" />
</Track.IncreaseRepeatButton>
</Track>
</ControlTemplate>
<ControlTemplate x:Key="HorizontalScrollBar" TargetType="{x:Type ScrollBar}">
<Track Name="PART_Track" Height="10" IsDirectionReversed="False" Width="Auto">
<Track.DecreaseRepeatButton>
<RepeatButton Margin="0,4.5" Template="{StaticResource RepeatButton}" />
</Track.DecreaseRepeatButton>
<Track.Thumb>
<Thumb Cursor="ScrollWE" Focusable="False" Margin="0,2.5" OverridesDefaultStyle="True" SnapsToDevicePixels="True" Template="{StaticResource Thumb}" />
</Track.Thumb>
<Track.IncreaseRepeatButton>
<RepeatButton Margin="0,4.5" Template="{StaticResource RepeatButton}" />
</Track.IncreaseRepeatButton>
</Track>
</ControlTemplate>
<ControlTemplate x:Key="SmallBarScrollViewer" TargetType="{x:Type ScrollViewer}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ScrollContentPresenter Grid.Column="0" Grid.Row="0" />
<ScrollBar Name="PART_VerticalScrollBar" Grid.Column="1" Grid.Row="0" Background="LightGray" OverridesDefaultStyle="True"
Value="{TemplateBinding VerticalOffset}" Maximum="{TemplateBinding ScrollableHeight}" Template="{StaticResource VerticalScrollBar}"
ViewportSize="{TemplateBinding ViewportHeight}" Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}" />
<ScrollBar Name="PART_HorizontalScrollBar" Grid.Column="0" Grid.Row="1" Background="LightGray" OverridesDefaultStyle="True" Orientation="Horizontal"
Value="{TemplateBinding HorizontalOffset}" Maximum="{TemplateBinding ScrollableWidth}" Template="{StaticResource HorizontalScrollBar}"
ViewportSize="{TemplateBinding ViewportWidth}" Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}" />
</Grid>
</ControlTemplate>
</ResourceDictionary>

View File

@@ -0,0 +1,259 @@
/*
* Copyright (c) 2024 ETH Zürich, IT Services
*
* 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 System.Threading;
using System.Windows;
using System.Windows.Media;
using FontAwesome.WPF;
using SafeExamBrowser.Applications.Contracts;
using SafeExamBrowser.Configuration.Contracts;
using SafeExamBrowser.Core.Contracts.Notifications;
using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.Proctoring.Contracts;
using SafeExamBrowser.Server.Contracts.Data;
using SafeExamBrowser.Settings.Browser;
using SafeExamBrowser.Settings.Proctoring;
using SafeExamBrowser.Settings.UserInterface;
using SafeExamBrowser.SystemComponents.Contracts.Audio;
using SafeExamBrowser.SystemComponents.Contracts.Keyboard;
using SafeExamBrowser.SystemComponents.Contracts.Network;
using SafeExamBrowser.SystemComponents.Contracts.PowerSupply;
using SafeExamBrowser.UserInterface.Contracts;
using SafeExamBrowser.UserInterface.Contracts.Browser;
using SafeExamBrowser.UserInterface.Contracts.Proctoring;
using SafeExamBrowser.UserInterface.Contracts.Shell;
using SafeExamBrowser.UserInterface.Contracts.Windows;
using SafeExamBrowser.UserInterface.Contracts.Windows.Data;
using SafeExamBrowser.UserInterface.Mobile.Windows;
using SplashScreen = SafeExamBrowser.UserInterface.Mobile.Windows.SplashScreen;
namespace SafeExamBrowser.UserInterface.Mobile
{
public class UserInterfaceFactory : IUserInterfaceFactory
{
private readonly IText text;
public UserInterfaceFactory(IText text)
{
this.text = text;
InitializeFontAwesome();
}
public IWindow CreateAboutWindow(AppConfig appConfig)
{
return new AboutWindow(appConfig, text);
}
public IActionCenter CreateActionCenter()
{
return new ActionCenter();
}
public IApplicationControl CreateApplicationControl(IApplication<IApplicationWindow> application, Location location)
{
if (location == Location.ActionCenter)
{
return new Controls.ActionCenter.ApplicationControl(application);
}
else
{
return new Controls.Taskbar.ApplicationControl(application);
}
}
public ISystemControl CreateAudioControl(IAudio audio, Location location)
{
if (location == Location.ActionCenter)
{
return new Controls.ActionCenter.AudioControl(audio, text);
}
else
{
return new Controls.Taskbar.AudioControl(audio, text);
}
}
public IBrowserWindow CreateBrowserWindow(IBrowserControl control, BrowserSettings settings, bool isMainWindow, ILogger logger)
{
return Application.Current.Dispatcher.Invoke(() => new BrowserWindow(control, settings, isMainWindow, text, logger));
}
public ICredentialsDialog CreateCredentialsDialog(CredentialsDialogPurpose purpose, string message, string title)
{
return Application.Current.Dispatcher.Invoke(() => new CredentialsDialog(purpose, message, title, text));
}
public IExamSelectionDialog CreateExamSelectionDialog(IEnumerable<Exam> exams)
{
return Application.Current.Dispatcher.Invoke(() => new ExamSelectionDialog(exams, text));
}
public ISystemControl CreateKeyboardLayoutControl(IKeyboard keyboard, Location location)
{
if (location == Location.ActionCenter)
{
return new Controls.ActionCenter.KeyboardLayoutControl(keyboard, text);
}
else
{
return new Controls.Taskbar.KeyboardLayoutControl(keyboard, text);
}
}
public ILockScreen CreateLockScreen(string message, string title, IEnumerable<LockScreenOption> options, LockScreenSettings settings)
{
return Application.Current.Dispatcher.Invoke(() => new LockScreen(message, title, settings, text, options));
}
public IWindow CreateLogWindow(ILogger logger)
{
var window = default(LogWindow);
var windowReadyEvent = new AutoResetEvent(false);
var windowThread = new Thread(() =>
{
window = new LogWindow(logger, text);
window.Closed += (o, args) => window.Dispatcher.InvokeShutdown();
window.Show();
windowReadyEvent.Set();
System.Windows.Threading.Dispatcher.Run();
});
windowThread.SetApartmentState(ApartmentState.STA);
windowThread.IsBackground = true;
windowThread.Start();
windowReadyEvent.WaitOne();
return window;
}
public ISystemControl CreateNetworkControl(INetworkAdapter adapter, Location location)
{
if (location == Location.ActionCenter)
{
return new Controls.ActionCenter.NetworkControl(adapter, text);
}
else
{
return new Controls.Taskbar.NetworkControl(adapter, text);
}
}
public INotificationControl CreateNotificationControl(INotification notification, Location location)
{
if (location == Location.ActionCenter)
{
return new Controls.ActionCenter.NotificationButton(notification);
}
else
{
return new Controls.Taskbar.NotificationButton(notification);
}
}
public IPasswordDialog CreatePasswordDialog(string message, string title)
{
return Application.Current.Dispatcher.Invoke(() => new PasswordDialog(message, title, text));
}
public IPasswordDialog CreatePasswordDialog(TextKey message, TextKey title)
{
return Application.Current.Dispatcher.Invoke(() => new PasswordDialog(text.Get(message), text.Get(title), text));
}
public ISystemControl CreatePowerSupplyControl(IPowerSupply powerSupply, Location location)
{
if (location == Location.ActionCenter)
{
return new Controls.ActionCenter.PowerSupplyControl(powerSupply, text);
}
else
{
return new Controls.Taskbar.PowerSupplyControl(powerSupply, text);
}
}
public IProctoringFinalizationDialog CreateProctoringFinalizationDialog()
{
return Application.Current.Dispatcher.Invoke(() => new ProctoringFinalizationDialog(text));
}
public IProctoringWindow CreateProctoringWindow(IProctoringControl control)
{
return Application.Current.Dispatcher.Invoke(() => new ProctoringWindow(control));
}
public INotificationControl CreateRaiseHandControl(IProctoringController controller, Location location, ProctoringSettings settings)
{
if (location == Location.ActionCenter)
{
return new Controls.ActionCenter.RaiseHandControl(controller, settings, text);
}
else
{
return new Controls.Taskbar.RaiseHandControl(controller, settings, text);
}
}
public IRuntimeWindow CreateRuntimeWindow(AppConfig appConfig)
{
return Application.Current.Dispatcher.Invoke(() => new RuntimeWindow(appConfig, text));
}
public IServerFailureDialog CreateServerFailureDialog(string info, bool showFallback)
{
return Application.Current.Dispatcher.Invoke(() => new ServerFailureDialog(info, showFallback, text));
}
public ISplashScreen CreateSplashScreen(AppConfig appConfig = null)
{
var window = default(SplashScreen);
var windowReadyEvent = new AutoResetEvent(false);
var windowThread = new Thread(() =>
{
window = new SplashScreen(text, appConfig);
window.Closed += (o, args) => window.Dispatcher.InvokeShutdown();
window.Show();
windowReadyEvent.Set();
System.Windows.Threading.Dispatcher.Run();
});
windowThread.SetApartmentState(ApartmentState.STA);
windowThread.IsBackground = true;
windowThread.Start();
windowReadyEvent.WaitOne();
return window;
}
public ITaskbar CreateTaskbar(ILogger logger)
{
return new Taskbar(logger);
}
public ITaskview CreateTaskview()
{
return new Taskview();
}
private void InitializeFontAwesome()
{
// To be able to use FontAwesome in XAML icon resources, we need to make sure that the FontAwesome.WPF assembly is loaded into
// the AppDomain before attempting to load an icon resource - thus the creation of an unused image below...
ImageAwesome.CreateImageSource(FontAwesomeIcon.FontAwesome, Brushes.Black);
}
}
}

View File

@@ -0,0 +1,48 @@
/*
* Copyright (c) 2024 ETH Zürich, IT Services
*
* 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.ComponentModel;
using System.Windows.Threading;
namespace SafeExamBrowser.UserInterface.Mobile.ViewModels
{
internal class DateTimeViewModel : INotifyPropertyChanged
{
private DispatcherTimer timer;
private readonly bool showSeconds;
public string Date { get; private set; }
public string Time { get; private set; }
public string ToolTip { get; private set; }
public event PropertyChangedEventHandler PropertyChanged;
public DateTimeViewModel(bool showSeconds)
{
this.showSeconds = showSeconds;
this.timer = new DispatcherTimer();
this.timer.Interval = TimeSpan.FromMilliseconds(250);
this.timer.Tick += Timer_Tick;
this.timer.Start();
}
private void Timer_Tick(object sender, EventArgs e)
{
var date = DateTime.Now;
Date = date.ToShortDateString();
Time = showSeconds ? date.ToLongTimeString() : date.ToShortTimeString();
ToolTip = $"{date.ToLongDateString()} {date.ToLongTimeString()}";
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Time)));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Date)));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ToolTip)));
}
}
}

View File

@@ -0,0 +1,109 @@
/*
* Copyright (c) 2024 ETH Zürich, IT Services
*
* 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.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Media;
using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.Settings.Logging;
namespace SafeExamBrowser.UserInterface.Mobile.ViewModels
{
internal class LogViewModel : ILogObserver
{
private IText text;
private ScrollViewer scrollViewer;
private TextBlock textBlock;
public bool AutoScroll { get; set; } = true;
public string AutoScrollTitle => text.Get(TextKey.LogWindow_AutoScroll);
public string TopmostTitle => text.Get(TextKey.LogWindow_AlwaysOnTop);
public string WindowTitle => text.Get(TextKey.LogWindow_Title);
public LogViewModel(IText text, ScrollViewer scrollViewer, TextBlock textBlock)
{
this.text = text;
this.scrollViewer = scrollViewer;
this.textBlock = textBlock;
}
public void Notify(ILogContent content)
{
switch (content)
{
case ILogText text:
AppendLogText(text);
break;
case ILogMessage message:
AppendLogMessage(message);
break;
default:
throw new NotImplementedException($"The log window is not yet implemented for log content of type {content.GetType()}!");
}
}
private void AppendLogText(ILogText logText)
{
textBlock.Dispatcher.InvokeAsync(() =>
{
var isHeader = logText.Text.StartsWith("/* ");
var isComment = logText.Text.StartsWith("# ");
var brush = isHeader || isComment ? Brushes.LimeGreen : textBlock.Foreground;
var fontWeight = isHeader ? FontWeights.Bold : FontWeights.Normal;
textBlock.Inlines.Add(new Run($"{logText.Text}{Environment.NewLine}") { FontWeight = fontWeight, Foreground = brush });
ScrollToEnd();
});
}
private void AppendLogMessage(ILogMessage message)
{
textBlock.Dispatcher.InvokeAsync(() =>
{
var date = message.DateTime.ToString("HH:mm:ss.fff");
var severity = message.Severity.ToString().ToUpper();
var threadId = message.ThreadInfo.Id < 10 ? $"0{message.ThreadInfo.Id}" : message.ThreadInfo.Id.ToString();
var threadName = message.ThreadInfo.HasName ? ": " + message.ThreadInfo.Name : string.Empty;
var threadInfo = $"[{threadId}{threadName}]";
var infoRun = new Run($"{date} {threadInfo} - ") { Foreground = Brushes.DarkGray };
var messageRun = new Run($"{severity}: {message.Message}{Environment.NewLine}") { Foreground = GetBrushFor(message.Severity) };
textBlock.Inlines.Add(infoRun);
textBlock.Inlines.Add(messageRun);
ScrollToEnd();
});
}
private Brush GetBrushFor(LogLevel severity)
{
switch (severity)
{
case LogLevel.Debug:
return Brushes.DarkGray;
case LogLevel.Error:
return Brushes.Red;
case LogLevel.Warning:
return Brushes.Orange;
default:
return Brushes.White;
}
}
private void ScrollToEnd()
{
if (AutoScroll)
{
scrollViewer.ScrollToEnd();
}
}
}
}

View File

@@ -0,0 +1,132 @@
/*
* Copyright (c) 2024 ETH Zürich, IT Services
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
using System.ComponentModel;
using System.Timers;
namespace SafeExamBrowser.UserInterface.Mobile.ViewModels
{
internal class ProgressIndicatorViewModel : INotifyPropertyChanged
{
private readonly object @lock = new object();
private Timer busyTimer;
private int currentProgress;
private bool isIndeterminate;
private int maxProgress;
private string status;
public event PropertyChangedEventHandler PropertyChanged;
public bool BusyIndication
{
set
{
HandleBusyIndication(value);
}
}
public int CurrentProgress
{
get
{
return currentProgress;
}
set
{
currentProgress = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(CurrentProgress)));
}
}
public bool IsIndeterminate
{
get
{
return isIndeterminate;
}
set
{
isIndeterminate = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsIndeterminate)));
}
}
public int MaxProgress
{
get
{
return maxProgress;
}
set
{
maxProgress = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(MaxProgress)));
}
}
public string Status
{
get
{
return status;
}
set
{
status = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Status)));
}
}
protected void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private void HandleBusyIndication(bool start)
{
lock (@lock)
{
if (busyTimer != null)
{
busyTimer.Elapsed -= BusyTimer_Elapsed;
busyTimer.Stop();
busyTimer.Close();
}
if (start)
{
busyTimer = new Timer
{
AutoReset = true,
Interval = 1500,
};
busyTimer.Elapsed += BusyTimer_Elapsed;
busyTimer.Start();
}
}
}
private void BusyTimer_Elapsed(object sender, ElapsedEventArgs e)
{
var next = Status ?? string.Empty;
if (next.EndsWith("..."))
{
next = Status.Substring(0, Status.Length - 3);
}
else
{
next += ".";
}
Status = next;
busyTimer.Interval = 750;
}
}
}

Some files were not shown because too many files have changed in this diff Show More