using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Microsoft.AppCenter; using Microsoft.AppCenter.Analytics; using Microsoft.AppCenter.Crashes; using Microsoft.Extensions.DependencyInjection; using Serilog; using Windows.ApplicationModel; using Windows.ApplicationModel.Activation; using Windows.ApplicationModel.Core; using Windows.Foundation.Metadata; using Windows.Storage; using Windows.System.Profile; using Windows.UI; using Windows.UI.ViewManagement; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Wino.Activation; using Wino.Core; using Wino.Core.Domain.Interfaces; using Wino.Core.Services; using Wino.Core.UWP; using Wino.Core.UWP.Services; using Wino.Mail.ViewModels; using Wino.Services; namespace Wino { public sealed partial class App : Application { private const string WinoLaunchLogPrefix = "[Wino Launch] "; private const string AppCenterKey = "90deb1d0-a77f-47d0-8a6b-7eaf111c6b72"; public new static App Current => (App)Application.Current; public IServiceProvider Services { get; } private readonly ILogInitializer _logInitializer; private readonly IThemeService _themeService; private readonly IDatabaseService _databaseService; private readonly IAppInitializerService _appInitializerService; private readonly IWinoSynchronizerFactory _synchronizerFactory; private readonly ITranslationService _translationService; // Order matters. private List initializeServices => new List() { _translationService, _databaseService, _themeService, _synchronizerFactory }; public App() { InitializeComponent(); UnhandledException += OnAppUnhandledException; EnteredBackground += OnEnteredBackground; LeavingBackground += OnLeavingBackground; Services = ConfigureServices(); _logInitializer = Services.GetService(); ConfigureLogger(); ConfigureAppCenter(); ConfigurePrelaunch(); ConfigureXbox(); _themeService = Services.GetService(); _databaseService = Services.GetService(); _appInitializerService = Services.GetService(); _synchronizerFactory = Services.GetService(); _translationService = Services.GetService(); Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); } private void LogActivation(string log) => Log.Information($"{WinoLaunchLogPrefix}{log}"); private void OnLeavingBackground(object sender, LeavingBackgroundEventArgs e) => LogActivation($"Wino went foreground."); private void OnEnteredBackground(object sender, EnteredBackgroundEventArgs e) => LogActivation($"Wino went background."); private IServiceProvider ConfigureServices() { var services = new ServiceCollection(); services.RegisterCoreServices(); services.RegisterCoreUWPServices(); RegisterUWPServices(services); RegisterViewModels(services); RegisterActivationHandlers(services); return services.BuildServiceProvider(); } #region Dependency Injection private void RegisterActivationHandlers(IServiceCollection services) { services.AddTransient(); services.AddTransient(); services.AddTransient(); services.AddTransient(); } private void RegisterUWPServices(IServiceCollection services) { services.AddSingleton, ApplicationResourceManager>(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); } private void RegisterViewModels(IServiceCollection services) { services.AddSingleton(typeof(AppShellViewModel)); services.AddTransient(typeof(SettingsDialogViewModel)); services.AddTransient(typeof(PersonalizationPageViewModel)); services.AddTransient(typeof(SettingOptionsPageViewModel)); services.AddTransient(typeof(MailListPageViewModel)); services.AddTransient(typeof(MailRenderingPageViewModel)); services.AddTransient(typeof(AccountManagementViewModel)); services.AddTransient(typeof(WelcomePageViewModel)); services.AddTransient(typeof(AboutPageViewModel)); services.AddTransient(typeof(ComposePageViewModel)); services.AddTransient(typeof(IdlePageViewModel)); services.AddTransient(typeof(SettingsPageViewModel)); services.AddTransient(typeof(NewAccountManagementPageViewModel)); services.AddTransient(typeof(AccountDetailsPageViewModel)); services.AddTransient(typeof(SignatureManagementPageViewModel)); services.AddTransient(typeof(MessageListPageViewModel)); services.AddTransient(typeof(ReadComposePanePageViewModel)); services.AddTransient(typeof(MergedAccountDetailsPageViewModel)); services.AddTransient(typeof(LanguageTimePageViewModel)); } #endregion #region Misc Configuration private void ConfigureLogger() => _logInitializer.SetupLogger(ApplicationData.Current.LocalFolder.Path); private void ConfigureAppCenter() => AppCenter.Start(AppCenterKey, typeof(Analytics), typeof(Crashes)); private void ConfigurePrelaunch() { if (ApiInformation.IsMethodPresent("Windows.ApplicationModel.Core.CoreApplication", "EnablePrelaunch")) CoreApplication.EnablePrelaunch(true); } private void ConfigureXbox() { // Xbox users should use Reveal focus. if (ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 6)) { FocusVisualKind = AnalyticsInfo.VersionInfo.DeviceFamily == "Xbox" ? FocusVisualKind.Reveal : FocusVisualKind.HighVisibility; } } private void ConfigureTitleBar() { var coreTitleBar = CoreApplication.GetCurrentView().TitleBar; var applicationViewTitleBar = ApplicationView.GetForCurrentView().TitleBar; // Extend shell content into core window to meet design requirements. coreTitleBar.ExtendViewIntoTitleBar = true; // Change system buttons and background colors to meet design requirements. applicationViewTitleBar.ButtonBackgroundColor = Colors.Transparent; applicationViewTitleBar.BackgroundColor = Colors.Transparent; applicationViewTitleBar.ButtonInactiveBackgroundColor = Colors.Transparent; applicationViewTitleBar.ButtonForegroundColor = Colors.White; } #endregion protected override void OnWindowCreated(WindowCreatedEventArgs args) { base.OnWindowCreated(args); LogActivation("Window is created."); ConfigureTitleBar(); } protected override async void OnLaunched(LaunchActivatedEventArgs args) { LogActivation($"OnLaunched -> {args.GetType().Name}, Kind -> {args.Kind}, PreviousExecutionState -> {args.PreviousExecutionState}, IsPrelaunch -> {args.PrelaunchActivated}"); if (!args.PrelaunchActivated) { await ActivateWinoAsync(args); } } protected override async void OnFileActivated(FileActivatedEventArgs args) { base.OnFileActivated(args); Log.Information($"File activation for {args.Files.Count} item(s)."); await ActivateWinoAsync(args); } protected override async void OnActivated(IActivatedEventArgs args) { base.OnActivated(args); Log.Information($"OnActivated -> {args.GetType().Name}, Kind -> {args.Kind}, Prev Execution State -> {args.PreviousExecutionState}"); await ActivateWinoAsync(args); } protected override async void OnBackgroundActivated(BackgroundActivatedEventArgs args) { base.OnBackgroundActivated(args); LogActivation($"OnBackgroundActivated -> {args.GetType().Name}, TaskInstanceIdName -> {args.TaskInstance?.Task?.Name ?? "NA"}"); await ActivateWinoAsync(args); } private void OnAppUnhandledException(object sender, Windows.UI.Xaml.UnhandledExceptionEventArgs e) { var parameters = new Dictionary() { { "BaseMessage", e.Exception.GetBaseException().Message }, { "BaseStackTrace", e.Exception.GetBaseException().StackTrace }, { "StackTrace", e.Exception.StackTrace }, { "Message", e.Exception.Message }, }; Log.Error(e.Exception, "[Wino Crash]"); Crashes.TrackError(e.Exception, parameters); Analytics.TrackEvent("Wino Crashed", parameters); } private bool IsInteractiveLaunchArgs(object args) => args is IActivatedEventArgs; private async Task ActivateWinoAsync(object args) { await PreInitializationAsync(); if (IsInteractiveLaunchArgs(args)) { if (Window.Current.Content == null) { var mainFrame = new Frame(); Window.Current.Content = mainFrame; await _themeService.InitializeAsync(); } } await HandleActivationAsync(args); if (IsInteractiveLaunchArgs(args)) { Window.Current.Activate(); LogActivation("Window activated"); } } /// /// Tasks that must run before the activation and launch. /// Regardless of whether it's an interactive launch or not. /// private async Task PreInitializationAsync() { // Handle migrations. // TODO: Automate migration process with more proper way. if (!ApplicationData.Current.LocalSettings.Values.ContainsKey("Migration_169")) { try { await _appInitializerService.MigrateAsync(); } catch (Exception ex) { Log.Error(ex, $"{WinoLaunchLogPrefix}Migration_169 failed."); } finally { ApplicationData.Current.LocalSettings.Values["Migration_169"] = true; } } foreach (var service in initializeServices) { await service.InitializeAsync(); } } private async Task HandleActivationAsync(object activationArgs) { var activationHandler = GetActivationHandlers().FirstOrDefault(h => h.CanHandle(activationArgs)); if (activationHandler != null) { await activationHandler.HandleAsync(activationArgs); } if (IsInteractiveLaunchArgs(activationArgs)) { var defaultHandler = new DefaultActivationHandler(); if (defaultHandler.CanHandle(activationArgs)) { await defaultHandler.HandleAsync(activationArgs); } } } private IEnumerable GetActivationHandlers() { yield return Services.GetService(); yield return Services.GetService(); yield return Services.GetService(); yield return Services.GetService(); } } }