Skip to content

Commit

Permalink
feat: add the UWP app launch (#386)
Browse files Browse the repository at this point in the history
* feat: add the UWP app launch

References: #347

* refactor: split common and platform-specific code

References: #347

* fix: fix async unhandled exception

References: #347
  • Loading branch information
al-kau authored Oct 28, 2024
1 parent 5478a06 commit e83cb1d
Show file tree
Hide file tree
Showing 9 changed files with 245 additions and 54 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
<ProjectGuid>{D392835A-52AE-4529-AAC0-52D3189CB461}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Eppie.App.Converters.UWP</RootNamespace>
<AssemblyName>Eppie.App.Converters.UWP</AssemblyName>
<RootNamespace>Eppie.App.Converters</RootNamespace>
<AssemblyName>Eppie.App.Converters</AssemblyName>
<DefaultLanguage>en-US</DefaultLanguage>
<TargetPlatformIdentifier>UAP</TargetPlatformIdentifier>
<TargetPlatformVersion Condition=" '$(TargetPlatformVersion)' == '' ">10.0.22621.0</TargetPlatformVersion>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
<GenerateLibraryLayout>true</GenerateLibraryLayout>
<ImplicitUsings>disable</ImplicitUsings>
<Nullable>disable</Nullable>
<AssemblyName>Eppie.App.Converters</AssemblyName>
<RootNamespace>Eppie.App.Converters</RootNamespace>

<!--
UnoFeatures let's you quickly add and manage implicit package references based on the features you want to use.
Expand Down
98 changes: 63 additions & 35 deletions source/Eppie.App/Eppie.App.Shared/App.UWP.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,22 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Threading.Tasks;
using Tuvi.App.Shared.Models;
using Tuvi.App.Shared.Services;
using Tuvi.App.Shared.Views;
using Tuvi.App.ViewModels;
using Tuvi.App.ViewModels.Services;
using Tuvi.OAuth2;
using Windows.ApplicationModel;
using Windows.ApplicationModel.Activation;
using Windows.Data.Json;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.Globalization;
using Windows.Storage;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
Expand All @@ -25,54 +35,53 @@ namespace Eppie.App.Shared
/// </summary>
public partial class App : Application
{
/// <summary>
/// Initializes the singleton application object. This is the first line of authored code
/// executed, and as such is the logical equivalent of main() or WinMain().
/// </summary>
public App()
{
this.InitializeComponent();
this.Suspending += OnSuspending;
}

/// <summary>
/// Invoked when the application is launched normally by the end user. Other entry points
/// will be used such as when the application is launched to open a specific file.
/// </summary>
/// <param name="e">Details about the launch request and process.</param>
protected override void OnLaunched(LaunchActivatedEventArgs e)
protected override async void OnLaunched(LaunchActivatedEventArgs e)
{
Frame rootFrame = Window.Current.Content as Frame;

// Do not repeat app initialization when the Window already has content,
// just ensure that the window is active
if (rootFrame == null)
try
{
// Create a Frame to act as the navigation context and navigate to the first page
rootFrame = new Frame();
Frame rootFrame = Window.Current.Content as Frame;

rootFrame.NavigationFailed += OnNavigationFailed;

if (e.PreviousExecutionState == ApplicationExecutionState.Terminated)
// Do not repeat app initialization when the Window already has content,
// just ensure that the window is active
if (rootFrame == null)
{
//TODO: Load state from previously suspended application
}
rootFrame = CreateRootFrame();

// Place the frame in the current Window
Window.Current.Content = rootFrame;
}
if (e.PreviousExecutionState == ApplicationExecutionState.Terminated)
{
//TODO: Load state from previously suspended application
}

if (e.PrelaunchActivated == false)
{
if (rootFrame.Content == null)
// Place the frame in the current Window
Window.Current.Content = rootFrame;
}

if (e.PrelaunchActivated == false)
{
// When the navigation stack isn't restored navigate to the first page,
// configuring the new page by passing required information as a navigation
// parameter
rootFrame.Navigate(typeof(Tuvi.App.Shared.Views.MainPage), e.Arguments);
if (rootFrame.Content == null)
{
// does database exist
if (await Core.IsFirstApplicationStartAsync().ConfigureAwait(true))
{
rootFrame.Navigate(typeof(WelcomePage));
}
else
{
rootFrame.Navigate(typeof(PasswordPage), PasswordActions.EnterPassword);
}
}
// Ensure the current window is active
Window.Current.Activate();
}
// Ensure the current window is active
Window.Current.Activate();
}
catch (Exception exception)
{
OnError(exception);
}
}

Expand All @@ -99,6 +108,25 @@ private void OnSuspending(object sender, SuspendingEventArgs e)
//TODO: Save application state and stop any background activity
deferral.Complete();
}

private Frame CreateRootFrame()
{
var frame = new Frame();
frame.NavigationFailed += OnNavigationFailed;

// ToDo: use nameof(Tuvi.App.Shared.Views) and add dot(.) inside NavigationService
NavigationService = new NavigationService(frame, "Tuvi.App.Shared.Views.");

_errorHandler = new ErrorHandler();
_errorHandler.SetMessageService(new MessageService());

return frame;
}

private void SubscribeToPlatformSpecificEvents()
{
Suspending += OnSuspending;
}
}
}

Expand Down
14 changes: 5 additions & 9 deletions source/Eppie.App/Eppie.App.Shared/App.Uno.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,6 @@ namespace Eppie.App.Shared
{
public partial class App : Application
{
/// <summary>
/// Initializes the singleton application object. This is the first line of authored code
/// executed, and as such is the logical equivalent of main() or WinMain().
/// </summary>
public App()
{
this.InitializeComponent();
}

protected Window MainWindow { get; private set; }
protected IHost Host { get; private set; }

Expand Down Expand Up @@ -100,6 +91,11 @@ protected override void OnLaunched(LaunchActivatedEventArgs args)
// Ensure the current window is active
MainWindow.Activate();
}

private void SubscribeToPlatformSpecificEvents()
{

}
}
}

Expand Down
6 changes: 6 additions & 0 deletions source/Eppie.App/Eppie.App.Shared/App.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@
<XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls" />
<!-- Load Uno.UI.Toolkit resources -->
<ToolkitResources xmlns="using:Uno.Toolkit.UI" />

<ResourceDictionary Source="ms-appx:///StaticResources/StaticStringResources.xaml" />
<ResourceDictionary Source="ms-appx:///Styles/NavigationViewStyle.xaml" />
<ResourceDictionary Source="ms-appx:///Styles/TuviMailStyles.xaml" />
<ResourceDictionary Source="ms-appx:///Eppie.App.Converters/ConvertersDictionary.xaml" />
<ResourceDictionary Source="ms-appx:///Converters/TuviMailConvertersDictionary.xaml" />
</ResourceDictionary.MergedDictionaries>

<!-- Add resources here -->
Expand Down
133 changes: 133 additions & 0 deletions source/Eppie.App/Eppie.App.Shared/App.xaml.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
using System;
using System.IO;
using System.Threading.Tasks;

using Tuvi.App.Shared.Models;
using Tuvi.App.Shared.Services;
using Tuvi.App.ViewModels.Services;
using Tuvi.Core;
using Tuvi.OAuth2;
using Windows.Globalization;
using Windows.Storage;

#if WINDOWS_UWP
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
#else
using Microsoft.UI.Xaml;
#endif
Expand All @@ -15,9 +24,133 @@ namespace Eppie.App.Shared
/// </summary>
public partial class App : Application
{
private static readonly string DataBaseFileName = "TuviMail.db";

private static readonly string DataFolder = ApplicationData.Current.LocalFolder.Path;

public INavigationService NavigationService { get; private set; }
public ITuviMail Core { get; private set; }
public ILocalSettingsService LocalSettingsService { get; private set; }
public AuthorizationProvider AuthProvider { get; private set; }
private NotificationManager _notificationManager { get; set; }

private ErrorHandler _errorHandler;

Check warning on line 37 in source/Eppie.App/Eppie.App.Shared/App.xaml.cs

View workflow job for this annotation

GitHub Actions / windows-latest './source/Eppie.App/Eppie.App/Eppie.App.csproj'

Field 'App._errorHandler' is never assigned to, and will always have its default value null


/// <summary>
/// Initializes the singleton application object. This is the first line of authored code
/// executed, and as such is the logical equivalent of main() or WinMain().
/// </summary>
public App()
{
InitializeLogging();
InitializeComponent();
SubscribeToEvents();
ConfigureServices();
}

private static void InitializeLogging()
{
// ToDo: Add logging
}

private void SubscribeToEvents()
{
SubscribeToPlatformSpecificEvents();

UnhandledException += CurrentApp_UnhandledException;
TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException;
}

private void ConfigureServices()
{
CreateAuth();
CreateCore();

LocalSettingsService = new LocalSettingsService();
ApplicationLanguages.PrimaryLanguageOverride = LocalSettingsService.Language;
}

private void CreateAuth()
{
AuthProvider = AuthorizationFactory.GetAuthorizationProvider(Tuvi.App.Shared.Authorization.AuthConfig.GetAuthorizationConfiguration());
}

private void CreateCore()
{
var tokenRefresher = AuthorizationFactory.GetTokenRefresher(AuthProvider);
Core = ComponentBuilder.Components.CreateTuviMailCore(Path.Combine(DataFolder, DataBaseFileName), new Tuvi.Core.ImplementationDetailsProvider("Tuvi seed", "Tuvi.Package", "[email protected]"), tokenRefresher);
_notificationManager = new NotificationManager(Core, OnError);
Core.WipeAllDataNeeded += OnWipeAllDataNeeded;
}

private void DisposeCore()
{
Core.WipeAllDataNeeded -= OnWipeAllDataNeeded;
if (Core is IDisposable d)
{
d.Dispose();
}
}

private async void OnWipeAllDataNeeded(object sender, EventArgs e)
{
try
{
// Dispose should be before await method
DisposeCore();
CreateCore();
await RemoveTempFilesAsync().ConfigureAwait(true);
}
catch (Exception ex)
{
OnError(ex);
}
}

private async Task RemoveTempFilesAsync()
{
var tempFolder = ApplicationData.Current.TemporaryFolder;
foreach (var item in await tempFolder.GetItemsAsync())
{
try
{
await item.DeleteAsync(StorageDeleteOption.PermanentDelete);
}
catch (Exception e)
{
OnError(e);
}
}
}

#if WINDOWS_UWP
private void CurrentApp_UnhandledException(object sender, Windows.UI.Xaml.UnhandledExceptionEventArgs e)
#else
private void CurrentApp_UnhandledException(object sender, Microsoft.UI.Xaml.UnhandledExceptionEventArgs e)
#endif
{
try
{
e.Handled = true;
OnError(e.Exception);
}
catch { }
}

private void TaskScheduler_UnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e)
{
try
{
e.SetObserved();
OnError(e.Exception);
}
catch { }
}

private void OnError(Exception exception)
{
_errorHandler?.OnError(exception, false);
}
}
}
19 changes: 19 additions & 0 deletions source/Eppie.App/Eppie.App.Shared/Authorization/AuthConfig.Uno.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#if !WINDOWS_UWP

using System;
using Finebits.Authorization.OAuth2.Google;
using Finebits.Authorization.OAuth2.Outlook;
using Tuvi.OAuth2;

namespace Tuvi.App.Shared.Authorization
{
internal static partial class AuthConfig
{
public static AuthorizationConfiguration GetAuthorizationConfiguration()
{
throw new NotImplementedException();
}
}
}

#endif
Loading

0 comments on commit e83cb1d

Please sign in to comment.