SEBWIN-405: Implemented server failure dialog.

This commit is contained in:
Damian Büchel 2020-07-24 18:22:22 +02:00
parent 60e8457033
commit 7915d4dff9
20 changed files with 304 additions and 20 deletions

View file

@ -165,6 +165,11 @@ namespace SafeExamBrowser.I18n.Contracts
PasswordDialog_SettingsPasswordRequired, PasswordDialog_SettingsPasswordRequired,
PasswordDialog_SettingsPasswordRequiredTitle, PasswordDialog_SettingsPasswordRequiredTitle,
RuntimeWindow_ApplicationRunning, RuntimeWindow_ApplicationRunning,
ServerFailureDialog_Abort,
ServerFailureDialog_Fallback,
ServerFailureDialog_Message,
ServerFailureDialog_Retry,
ServerFailureDialog_Title,
Shell_QuitButton, Shell_QuitButton,
SystemControl_AudioDeviceInfo, SystemControl_AudioDeviceInfo,
SystemControl_AudioDeviceInfoMuted, SystemControl_AudioDeviceInfoMuted,

View file

@ -453,6 +453,21 @@
<Entry key="RuntimeWindow_ApplicationRunning"> <Entry key="RuntimeWindow_ApplicationRunning">
SEB wird ausgeführt. SEB wird ausgeführt.
</Entry> </Entry>
<Entry key="ServerFailureDialog_Abort">
Abbrechen
</Entry>
<Entry key="ServerFailureDialog_Fallback">
Fallback
</Entry>
<Entry key="ServerFailureDialog_Message">
Bei der Kommunikation mit dem SEB-Server ist ein Fehler aufgetreten.
</Entry>
<Entry key="ServerFailureDialog_Retry">
Wiederholen
</Entry>
<Entry key="ServerFailureDialog_Title">
SEB-Server-Fehler
</Entry>
<Entry key="Shell_QuitButton"> <Entry key="Shell_QuitButton">
Sitzung beenden Sitzung beenden
</Entry> </Entry>

View file

@ -453,6 +453,21 @@
<Entry key="RuntimeWindow_ApplicationRunning"> <Entry key="RuntimeWindow_ApplicationRunning">
SEB is running. SEB is running.
</Entry> </Entry>
<Entry key="ServerFailureDialog_Abort">
Abort
</Entry>
<Entry key="ServerFailureDialog_Fallback">
Fallback
</Entry>
<Entry key="ServerFailureDialog_Message">
An error occurred while trying to communicate with the SEB server.
</Entry>
<Entry key="ServerFailureDialog_Retry">
Retry
</Entry>
<Entry key="ServerFailureDialog_Title">
SEB-Server Error
</Entry>
<Entry key="Shell_QuitButton"> <Entry key="Shell_QuitButton">
Terminate Session Terminate Session
</Entry> </Entry>

View file

@ -16,10 +16,12 @@ namespace SafeExamBrowser.Runtime.Operations.Events
public bool Fallback { get; set; } public bool Fallback { get; set; }
public string Message { get; set; } public string Message { get; set; }
public bool Retry { get; set; } public bool Retry { get; set; }
public bool ShowFallback { get; }
public ServerFailureEventArgs(string message) public ServerFailureEventArgs(string message, bool showFallback)
{ {
Message = message; Message = message;
ShowFallback = showFallback;
} }
} }
} }

View file

@ -70,6 +70,7 @@ namespace SafeExamBrowser.Runtime.Operations
if (status == LoadStatus.Success) if (status == LoadStatus.Success)
{ {
Context.Next.Settings = settings; Context.Next.Settings = settings;
Context.Next.Settings.Browser.StartUrl = exam.Url;
result = OperationResult.Success; result = OperationResult.Success;
} }
else else
@ -116,7 +117,7 @@ namespace SafeExamBrowser.Runtime.Operations
{ {
var result = OperationResult.Failed; var result = OperationResult.Failed;
if (Context.Current.Settings.SessionMode == SessionMode.Server) if (Context.Current?.Settings.SessionMode == SessionMode.Server)
{ {
logger.Info("Finalizing server..."); logger.Info("Finalizing server...");
StatusChanged?.Invoke(TextKey.OperationStatus_FinalizeServer); StatusChanged?.Invoke(TextKey.OperationStatus_FinalizeServer);
@ -192,7 +193,7 @@ namespace SafeExamBrowser.Runtime.Operations
private bool Retry(string message, out bool abort, out bool fallback) private bool Retry(string message, out bool abort, out bool fallback)
{ {
var args = new ServerFailureEventArgs(message); var args = new ServerFailureEventArgs(message, Context.Next.Settings.Server.PerformFallback);
ActionRequired?.Invoke(args); ActionRequired?.Invoke(args);

View file

@ -417,7 +417,18 @@ namespace SafeExamBrowser.Runtime
private void AskForServerFailureAction(ServerFailureEventArgs args) private void AskForServerFailureAction(ServerFailureEventArgs args)
{ {
// TODO: Also implement mechanism to retrieve selection via client!! var isStartup = !SessionIsRunning;
var isRunningOnDefaultDesktop = SessionIsRunning && Session.Settings.Security.KioskMode == KioskMode.DisableExplorerShell;
if (isStartup || isRunningOnDefaultDesktop)
{
TryAskForServerFailureActionViaDialog(args);
}
else
{
// TODO: Also implement mechanism to retrieve selection via client!!
// TryAskForServerFailureActionViaClient(args);
}
} }
private void AskIfConfigurationSufficient(ConfigurationCompletedEventArgs args) private void AskIfConfigurationSufficient(ConfigurationCompletedEventArgs args)
@ -503,15 +514,23 @@ namespace SafeExamBrowser.Runtime
private void TryAskForExamSelectionViaDialog(ExamSelectionEventArgs args) private void TryAskForExamSelectionViaDialog(ExamSelectionEventArgs args)
{ {
var message = TextKey.ExamSelectionDialog_Message; var dialog = uiFactory.CreateExamSelectionDialog(args.Exams);
var title = TextKey.ExamSelectionDialog_Title;
var dialog = uiFactory.CreateExamSelectionDialog(text.Get(message), text.Get(title), args.Exams);
var result = dialog.Show(runtimeWindow); var result = dialog.Show(runtimeWindow);
args.SelectedExam = result.SelectedExam; args.SelectedExam = result.SelectedExam;
args.Success = result.Success; args.Success = result.Success;
} }
private void TryAskForServerFailureActionViaDialog(ServerFailureEventArgs args)
{
var dialog = uiFactory.CreateServerFailureDialog(args.Message, args.ShowFallback);
var result = dialog.Show(runtimeWindow);
args.Abort = result.Abort;
args.Fallback = result.Fallback;
args.Retry = result.Retry;
}
private void TryGetPasswordViaDialog(PasswordRequiredEventArgs args) private void TryGetPasswordViaDialog(PasswordRequiredEventArgs args)
{ {
var message = default(TextKey); var message = default(TextKey);

View file

@ -18,6 +18,11 @@ namespace SafeExamBrowser.Server.Contracts
/// </summary> /// </summary>
public string Id { get; set; } public string Id { get; set; }
/// <summary>
/// The name of the learning management system (LMS) on which the exam is running.
/// </summary>
public string LmsName { get; set; }
/// <summary> /// <summary>
/// The name of the exam. /// The name of the exam.
/// </summary> /// </summary>

View file

@ -223,6 +223,7 @@ namespace SafeExamBrowser.Server
exams.Add(new Exam exams.Add(new Exam
{ {
Id = exam["examId"].Value<string>(), Id = exam["examId"].Value<string>(),
LmsName = exam["lmsType"].Value<string>(),
Name = exam["name"].Value<string>(), Name = exam["name"].Value<string>(),
Url = exam["url"].Value<string>() Url = exam["url"].Value<string>()
}); });
@ -314,7 +315,7 @@ namespace SafeExamBrowser.Server
private string ToString(HttpResponseMessage response) private string ToString(HttpResponseMessage response)
{ {
return $"{(int) response.StatusCode} {response.StatusCode} {response.ReasonPhrase}"; return $"{(int?) response?.StatusCode} {response?.StatusCode} {response?.ReasonPhrase}";
} }
} }
} }

View file

@ -58,7 +58,7 @@ namespace SafeExamBrowser.UserInterface.Contracts
/// <summary> /// <summary>
/// Creates an exam selection dialog for the given exams. /// Creates an exam selection dialog for the given exams.
/// </summary> /// </summary>
IExamSelectionDialog CreateExamSelectionDialog(string message, string title, IEnumerable<Exam> exams); IExamSelectionDialog CreateExamSelectionDialog(IEnumerable<Exam> exams);
/// <summary> /// <summary>
/// Creates a system control which allows to change the keyboard layout of the computer. /// Creates a system control which allows to change the keyboard layout of the computer.
@ -98,9 +98,13 @@ namespace SafeExamBrowser.UserInterface.Contracts
/// <summary> /// <summary>
/// Creates a new runtime window which runs on its own thread. /// Creates a new runtime window which runs on its own thread.
/// </summary> /// </summary>
/// <returns></returns>
IRuntimeWindow CreateRuntimeWindow(AppConfig appConfig); IRuntimeWindow CreateRuntimeWindow(AppConfig appConfig);
/// <summary>
/// Creates a new server failure dialog with the given parameters.
/// </summary>
IServerFailureDialog CreateServerFailureDialog(string info, bool showFallback);
/// <summary> /// <summary>
/// Creates a new splash screen which runs on its own thread. /// Creates a new splash screen which runs on its own thread.
/// </summary> /// </summary>

View file

@ -89,12 +89,14 @@
<Compile Include="Windows\Data\ExamSelectionDialogResult.cs" /> <Compile Include="Windows\Data\ExamSelectionDialogResult.cs" />
<Compile Include="Windows\Data\LockScreenOption.cs" /> <Compile Include="Windows\Data\LockScreenOption.cs" />
<Compile Include="Windows\Data\LockScreenResult.cs" /> <Compile Include="Windows\Data\LockScreenResult.cs" />
<Compile Include="Windows\Data\ServerFailureDialogResult.cs" />
<Compile Include="Windows\Events\WindowClosingEventHandler.cs" /> <Compile Include="Windows\Events\WindowClosingEventHandler.cs" />
<Compile Include="Windows\IExamSelectionDialog.cs" /> <Compile Include="Windows\IExamSelectionDialog.cs" />
<Compile Include="Windows\ILockScreen.cs" /> <Compile Include="Windows\ILockScreen.cs" />
<Compile Include="Windows\IPasswordDialog.cs" /> <Compile Include="Windows\IPasswordDialog.cs" />
<Compile Include="Windows\Data\PasswordDialogResult.cs" /> <Compile Include="Windows\Data\PasswordDialogResult.cs" />
<Compile Include="Windows\IRuntimeWindow.cs" /> <Compile Include="Windows\IRuntimeWindow.cs" />
<Compile Include="Windows\IServerFailureDialog.cs" />
<Compile Include="Windows\ISplashScreen.cs" /> <Compile Include="Windows\ISplashScreen.cs" />
<Compile Include="Windows\IWindow.cs" /> <Compile Include="Windows\IWindow.cs" />
</ItemGroup> </ItemGroup>

View file

@ -0,0 +1,36 @@
/*
* Copyright (c) 2020 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
namespace SafeExamBrowser.UserInterface.Contracts.Windows.Data
{
/// <summary>
/// Defines the user interaction result of an <see cref="IServerFailureDialog"/>.
/// </summary>
public class ServerFailureDialogResult
{
/// <summary>
/// Indicates whether the user wants to abort the operation.
/// </summary>
public bool Abort { get; set; }
/// <summary>
/// Indicates whether the user wants to performa a fallback.
/// </summary>
public bool Fallback { get; set; }
/// <summary>
/// Indicates whether the user wants to retry the operation.
/// </summary>
public bool Retry { get; set; }
/// <summary>
/// Indicates whether the user confirmed the dialog or not.
/// </summary>
public bool Success { get; set; }
}
}

View file

@ -11,7 +11,7 @@ using SafeExamBrowser.UserInterface.Contracts.Windows.Data;
namespace SafeExamBrowser.UserInterface.Contracts.Windows namespace SafeExamBrowser.UserInterface.Contracts.Windows
{ {
/// <summary> /// <summary>
/// Defines the functionality of an exam selection dialog. /// The dialog shown to let the user select which server exam to start.
/// </summary> /// </summary>
public interface IExamSelectionDialog public interface IExamSelectionDialog
{ {

View file

@ -0,0 +1,23 @@
/*
* Copyright (c) 2020 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
using SafeExamBrowser.UserInterface.Contracts.Windows.Data;
namespace SafeExamBrowser.UserInterface.Contracts.Windows
{
/// <summary>
/// The dialog shown in case a communication failure with a server occurs.
/// </summary>
public interface IServerFailureDialog
{
/// <summary>
/// Shows the dialog as topmost window. If a parent window is specified, the dialog is rendered modally for the given parent.
/// </summary>
ServerFailureDialogResult Show(IWindow parent = null);
}
}

View file

@ -168,6 +168,9 @@
<Compile Include="Windows\RuntimeWindow.xaml.cs"> <Compile Include="Windows\RuntimeWindow.xaml.cs">
<DependentUpon>RuntimeWindow.xaml</DependentUpon> <DependentUpon>RuntimeWindow.xaml</DependentUpon>
</Compile> </Compile>
<Compile Include="Windows\ServerFailureDialog.xaml.cs">
<DependentUpon>ServerFailureDialog.xaml</DependentUpon>
</Compile>
<Compile Include="Windows\SplashScreen.xaml.cs"> <Compile Include="Windows\SplashScreen.xaml.cs">
<DependentUpon>SplashScreen.xaml</DependentUpon> <DependentUpon>SplashScreen.xaml</DependentUpon>
</Compile> </Compile>
@ -347,6 +350,10 @@
<SubType>Designer</SubType> <SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
</Page> </Page>
<Page Include="Windows\ServerFailureDialog.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Windows\Taskview.xaml"> <Page Include="Windows\Taskview.xaml">
<SubType>Designer</SubType> <SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>

View file

@ -82,9 +82,9 @@ namespace SafeExamBrowser.UserInterface.Desktop
return Application.Current.Dispatcher.Invoke(() => new BrowserWindow(control, settings, isMainWindow, text)); return Application.Current.Dispatcher.Invoke(() => new BrowserWindow(control, settings, isMainWindow, text));
} }
public IExamSelectionDialog CreateExamSelectionDialog(string message, string title, IEnumerable<Exam> exams) public IExamSelectionDialog CreateExamSelectionDialog(IEnumerable<Exam> exams)
{ {
return Application.Current.Dispatcher.Invoke(() => new ExamSelectionDialog(message, title, text, exams)); return Application.Current.Dispatcher.Invoke(() => new ExamSelectionDialog(exams, text));
} }
public ISystemControl CreateKeyboardLayoutControl(IKeyboard keyboard, Location location) public ISystemControl CreateKeyboardLayoutControl(IKeyboard keyboard, Location location)
@ -167,6 +167,11 @@ namespace SafeExamBrowser.UserInterface.Desktop
return Application.Current.Dispatcher.Invoke(() => new RuntimeWindow(appConfig, text)); 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) public ISplashScreen CreateSplashScreen(AppConfig appConfig = null)
{ {
var window = default(SplashScreen); var window = default(SplashScreen);

View file

@ -39,7 +39,7 @@
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<TextBlock Margin="0,0,5,0" Text="{Binding Id}" /> <TextBlock Margin="0,0,5,0" Text="{Binding Id}" />
<TextBlock Margin="0,0,5,0" Text="-" /> <TextBlock Margin="0,0,5,0" Text="-" />
<TextBlock FontStyle="Italic" Text="{Binding Url}" /> <TextBlock FontStyle="Italic" Text="{Binding LmsName}" />
</StackPanel> </StackPanel>
</StackPanel> </StackPanel>
</DataTemplate> </DataTemplate>

View file

@ -20,12 +20,12 @@ namespace SafeExamBrowser.UserInterface.Desktop.Windows
{ {
private readonly IText text; private readonly IText text;
public ExamSelectionDialog(string message, string title, IText text, IEnumerable<Exam> exams) public ExamSelectionDialog(IEnumerable<Exam> exams, IText text)
{ {
this.text = text; this.text = text;
InitializeComponent(); InitializeComponent();
InitializeExamSelectionDialog(message, title, exams); InitializeExamSelectionDialog(exams);
} }
public ExamSelectionDialogResult Show(IWindow parent = null) public ExamSelectionDialogResult Show(IWindow parent = null)
@ -50,10 +50,10 @@ namespace SafeExamBrowser.UserInterface.Desktop.Windows
}); });
} }
private void InitializeExamSelectionDialog(string message, string title, IEnumerable<Exam> exams) private void InitializeExamSelectionDialog(IEnumerable<Exam> exams)
{ {
Message.Text = message; Message.Text = text.Get(TextKey.ExamSelectionDialog_Message);
Title = title; Title = text.Get(TextKey.ExamSelectionDialog_Title);
WindowStartupLocation = WindowStartupLocation.CenterScreen; WindowStartupLocation = WindowStartupLocation.CenterScreen;
CancelButton.Content = text.Get(TextKey.ExamSelectionDialog_Cancel); CancelButton.Content = text.Get(TextKey.ExamSelectionDialog_Cancel);

View file

@ -0,0 +1,42 @@
<Window x:Class="SafeExamBrowser.UserInterface.Desktop.Windows.ServerFailureDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:fa="http://schemas.fontawesome.io/icons/"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:SafeExamBrowser.UserInterface.Desktop.Windows"
mc:Ignorable="d" Height="250" Width="450" ResizeMode="NoResize" Topmost="True">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="../Templates/Colors.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Grid FocusManager.FocusedElement="{Binding ElementName=RetryButton}">
<Grid.RowDefinitions>
<RowDefinition Height="2*" />
<RowDefinition Height="1*" />
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<fa:ImageAwesome Grid.Column="0" Foreground="LightGray" Icon="Warning" Margin="25" Width="50" />
<WrapPanel Grid.Column="1" Margin="0,0,25,0" Orientation="Vertical" VerticalAlignment="Center">
<TextBlock x:Name="Message" TextWrapping="WrapWithOverflow" />
<TextBlock x:Name="Info" FontFamily="Courier New" Margin="0,10,0,0" TextWrapping="WrapWithOverflow" />
</WrapPanel>
</Grid>
</Grid>
<Grid Grid.Row="1" Background="{StaticResource BackgroundBrush}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<WrapPanel Orientation="Horizontal" Margin="25,0" HorizontalAlignment="Right" VerticalAlignment="Center">
<Button x:Name="RetryButton" Cursor="Hand" Margin="10,0" Padding="10,5" MinWidth="75" />
<Button x:Name="FallbackButton" Cursor="Hand" Margin="0,0,10,0" Padding="10,5" MinWidth="75" />
<Button x:Name="AbortButton" Cursor="Hand" Padding="10,5" MinWidth="75" />
</WrapPanel>
</Grid>
</Grid>
</Window>

View file

@ -0,0 +1,96 @@
/*
* Copyright (c) 2020 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
using System.Windows;
using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.UserInterface.Contracts.Windows;
using SafeExamBrowser.UserInterface.Contracts.Windows.Data;
namespace SafeExamBrowser.UserInterface.Desktop.Windows
{
public partial class ServerFailureDialog : Window, IServerFailureDialog
{
private readonly IText text;
public ServerFailureDialog(string info, bool showFallback, IText text)
{
this.text = text;
InitializeComponent();
InitializeDialog(info, showFallback);
}
public ServerFailureDialogResult Show(IWindow parent = null)
{
return Dispatcher.Invoke(() =>
{
var result = new ServerFailureDialogResult { Success = false };
if (parent is Window)
{
Owner = parent as Window;
WindowStartupLocation = WindowStartupLocation.CenterOwner;
}
if (ShowDialog() is true)
{
result.Abort = Tag as string == nameof(AbortButton);
result.Fallback = Tag as string == nameof(FallbackButton);
result.Retry = Tag as string == nameof(RetryButton);
result.Success = true;
}
else
{
result.Abort = true;
}
return result;
});
}
private void InitializeDialog(string info, bool showFallback)
{
Info.Text = info;
Message.Text = text.Get(TextKey.ServerFailureDialog_Message);
Title = text.Get(TextKey.ServerFailureDialog_Title);
AbortButton.Click += AbortButton_Click;
AbortButton.Content = text.Get(TextKey.ServerFailureDialog_Abort);
FallbackButton.Click += FallbackButton_Click;
FallbackButton.Content = text.Get(TextKey.ServerFailureDialog_Fallback);
FallbackButton.Visibility = showFallback ? Visibility.Visible : Visibility.Collapsed;
Loaded += (o, args) => Activate();
RetryButton.Click += RetryButton_Click;
RetryButton.Content = text.Get(TextKey.ServerFailureDialog_Retry);
}
private void AbortButton_Click(object sender, RoutedEventArgs e)
{
DialogResult = true;
Tag = nameof(AbortButton);
Close();
}
private void FallbackButton_Click(object sender, RoutedEventArgs e)
{
DialogResult = true;
Tag = nameof(FallbackButton);
Close();
}
private void RetryButton_Click(object sender, RoutedEventArgs e)
{
DialogResult = true;
Tag = nameof(RetryButton);
Close();
}
}
}

View file

@ -82,7 +82,7 @@ namespace SafeExamBrowser.UserInterface.Mobile
return Application.Current.Dispatcher.Invoke(() => new BrowserWindow(control, settings, isMainWindow, text)); return Application.Current.Dispatcher.Invoke(() => new BrowserWindow(control, settings, isMainWindow, text));
} }
public IExamSelectionDialog CreateExamSelectionDialog(string message, string title, IEnumerable<Exam> exams) public IExamSelectionDialog CreateExamSelectionDialog(IEnumerable<Exam> exams)
{ {
// TODO // TODO
throw new System.NotImplementedException(); throw new System.NotImplementedException();
@ -168,6 +168,12 @@ namespace SafeExamBrowser.UserInterface.Mobile
return Application.Current.Dispatcher.Invoke(() => new RuntimeWindow(appConfig, text)); return Application.Current.Dispatcher.Invoke(() => new RuntimeWindow(appConfig, text));
} }
public IServerFailureDialog CreateServerFailureDialog(string info, bool showFallback)
{
// TODO
throw new System.NotImplementedException();
}
public ISplashScreen CreateSplashScreen(AppConfig appConfig = null) public ISplashScreen CreateSplashScreen(AppConfig appConfig = null)
{ {
var window = default(SplashScreen); var window = default(SplashScreen);