From f5acc965e5f7d3bd1d2fd34a6e1f40fe4b87f319 Mon Sep 17 00:00:00 2001 From: Shane Neuville Date: Wed, 21 Jul 2021 19:57:16 -0500 Subject: [PATCH] Window lifecycle (#1754) * Window Life Cycle * - fix benchmark stubs * - fix device tests * - keep reference to associated window * - fix window call * - fix window --- .../src/AppHostBuilderExtensions.Android.cs | 36 +++++ .../src/AppHostBuilderExtensions.Standard.cs | 12 ++ .../src/AppHostBuilderExtensions.Windows.cs | 50 +++++++ .../Core/src/AppHostBuilderExtensions.cs | 76 +--------- .../Core/src/AppHostBuilderExtensions.iOS.cs | 35 +++++ src/Compatibility/Core/src/Windows/Forms.cs | 10 +- .../src/Windows/NavigationPageRenderer.cs | 13 +- .../Core/src/Windows/Platform.cs | 18 +-- .../Core/src/Windows/WindowsBasePage.cs | 136 ++++++++--------- .../Core/src/Windows/WindowsPage.cs | 10 -- src/Controls/src/Core/Application.cs | 12 +- .../src/Core/HandlerImpl/Window.Impl.cs | 141 ++++++++++++------ .../GestureManager/GestureManager.iOS.cs | 5 +- .../Platform/iOS/UIApplicationExtensions.cs | 27 ---- src/Core/src/Core/IWindow.cs | 12 ++ src/Core/src/Hosting/AppHost.cs | 2 + .../AppHostBuilderExtensions.Android.cs | 49 ++++++ .../AppHostBuilderExtensions.Standard.cs | 12 ++ .../AppHostBuilderExtensions.Windows.cs | 47 ++++++ .../AppHostBuilderExtensions.cs | 0 .../AppHostBuilderExtensions.iOS.cs | 42 ++++++ .../Windows/WindowsLifecycle.cs | 4 +- .../WindowsLifecycleBuilderExtensions.cs | 2 + .../src/Platform/Android/ContextExtensions.cs | 16 ++ .../Platform/Android/MauiAppCompatActivity.cs | 13 ++ .../Platform/Windows/MauiWinUIApplication.cs | 4 +- .../src/Platform/Windows/MauiWinUIWindow.cs | 10 ++ .../src/Platform/Windows/WindowExtensions.cs | 15 +- .../Platform/iOS/MauiUIApplicationDelegate.cs | 13 +- .../Platform/iOS/UIApplicationExtensions.cs | 37 +++++ src/Core/src/TaskExtensions.cs | 6 +- src/Core/tests/Benchmarks/Stubs/WindowStub.cs | 42 ++++++ .../tests/DeviceTests/Stubs/WindowStub.cs | 41 +++++ 33 files changed, 683 insertions(+), 265 deletions(-) create mode 100644 src/Compatibility/Core/src/AppHostBuilderExtensions.Android.cs create mode 100644 src/Compatibility/Core/src/AppHostBuilderExtensions.Standard.cs create mode 100644 src/Compatibility/Core/src/AppHostBuilderExtensions.Windows.cs create mode 100644 src/Compatibility/Core/src/AppHostBuilderExtensions.iOS.cs delete mode 100644 src/Compatibility/Core/src/Windows/WindowsPage.cs delete mode 100644 src/Controls/src/Core/Platform/iOS/UIApplicationExtensions.cs create mode 100644 src/Core/src/Hosting/LifecycleEvents/AppHostBuilderExtensions.Android.cs create mode 100644 src/Core/src/Hosting/LifecycleEvents/AppHostBuilderExtensions.Standard.cs create mode 100644 src/Core/src/Hosting/LifecycleEvents/AppHostBuilderExtensions.Windows.cs rename src/Core/src/{ => Hosting}/LifecycleEvents/AppHostBuilderExtensions.cs (100%) create mode 100644 src/Core/src/Hosting/LifecycleEvents/AppHostBuilderExtensions.iOS.cs create mode 100644 src/Core/src/Platform/iOS/UIApplicationExtensions.cs diff --git a/src/Compatibility/Core/src/AppHostBuilderExtensions.Android.cs b/src/Compatibility/Core/src/AppHostBuilderExtensions.Android.cs new file mode 100644 index 000000000000..8fbf113a0c14 --- /dev/null +++ b/src/Compatibility/Core/src/AppHostBuilderExtensions.Android.cs @@ -0,0 +1,36 @@ +using Microsoft.Maui.LifecycleEvents; +using Microsoft.Maui.Hosting; +using Microsoft.Maui.Controls.Compatibility; + +namespace Microsoft.Maui.Controls.Hosting +{ + public static partial class AppHostBuilderExtensions + { + internal static IAppHostBuilder ConfigureCompatibilityLifecycleEvents(this IAppHostBuilder builder) => + builder.ConfigureLifecycleEvents(events => events.AddAndroid(OnConfigureLifeCycle)); + + static void OnConfigureLifeCycle(IAndroidLifecycleBuilder android) + { + android + .OnApplicationCreating((app) => + { + // This is the initial Init to set up any system services registered by + // Forms.Init(). This happens in the Application's OnCreate - before + // any UI has appeared. + // This creates a dummy MauiContext that wraps the Application. + + var services = MauiApplication.Current.Services; + var mauiContext = new MauiContext(services, app); + var state = new ActivationState(mauiContext); + Forms.Init(state, new InitializationOptions { Flags = InitializationFlags.SkipRenderers }); + }) + .OnMauiContextCreated((mauiContext) => + { + // This is the final Init that sets up the real context from the activity. + + var state = new ActivationState(mauiContext); + Forms.Init(state); + }); + } + } +} diff --git a/src/Compatibility/Core/src/AppHostBuilderExtensions.Standard.cs b/src/Compatibility/Core/src/AppHostBuilderExtensions.Standard.cs new file mode 100644 index 000000000000..ed399c0cf58f --- /dev/null +++ b/src/Compatibility/Core/src/AppHostBuilderExtensions.Standard.cs @@ -0,0 +1,12 @@ +using Microsoft.Maui.Hosting; +using Microsoft.Maui.LifecycleEvents; +using System; + +namespace Microsoft.Maui.Controls.Hosting +{ + public static partial class AppHostBuilderExtensions + { + internal static IAppHostBuilder ConfigureCompatibilityLifecycleEvents(this IAppHostBuilder builder) => + builder; + } +} diff --git a/src/Compatibility/Core/src/AppHostBuilderExtensions.Windows.cs b/src/Compatibility/Core/src/AppHostBuilderExtensions.Windows.cs new file mode 100644 index 000000000000..8308f1fb5536 --- /dev/null +++ b/src/Compatibility/Core/src/AppHostBuilderExtensions.Windows.cs @@ -0,0 +1,50 @@ +#nullable enable +using Microsoft.Maui.Controls.Compatibility.Platform.UWP; +using Microsoft.Maui.Graphics.Win2D; +using BoxRenderer = Microsoft.Maui.Controls.Compatibility.Platform.UWP.BoxViewBorderRenderer; +using CellRenderer = Microsoft.Maui.Controls.Compatibility.Platform.UWP.TextCellRenderer; +using Deserializer = Microsoft.Maui.Controls.Compatibility.Platform.UWP.WindowsSerializer; +using ResourcesProvider = Microsoft.Maui.Controls.Compatibility.Platform.UWP.WindowsResourcesProvider; +using StreamImagesourceHandler = Microsoft.Maui.Controls.Compatibility.Platform.UWP.StreamImageSourceHandler; +using ImageLoaderSourceHandler = Microsoft.Maui.Controls.Compatibility.Platform.UWP.UriImageSourceHandler; +using DefaultRenderer = Microsoft.Maui.Controls.Compatibility.Platform.UWP.DefaultRenderer; +using Microsoft.Maui.LifecycleEvents; +using Microsoft.Maui.Controls.Compatibility; +using System; +using Microsoft.Maui.Hosting; + +namespace Microsoft.Maui.Controls.Hosting +{ + public static partial class AppHostBuilderExtensions + { + internal static IAppHostBuilder ConfigureCompatibilityLifecycleEvents(this IAppHostBuilder builder) => + builder.ConfigureLifecycleEvents(events => events.AddWindows(OnConfigureLifeCycle)); + + static void OnConfigureLifeCycle(IWindowsLifecycleBuilder windows) + { + windows.OnLaunching((app, args) => + { + // This is the initial Init to set up any system services registered by + // Forms.Init(). This happens before any UI has appeared. + // This creates a dummy MauiContext. + // We need to call this so the Window and Root Page can new up successfully + // The dispatcher that's inside of Forms.Init needs to be setup before the initial + // window and root page start creating. + // Inside OnLaunched we grab the MauiContext that's on the window so we can have the correct + // MauiContext inside Forms + + var services = MauiWinUIApplication.Current.Services; + var mauiContext = new MauiContext(services); + var state = new ActivationState(mauiContext, args); + Forms.Init(state, new InitializationOptions { Flags = InitializationFlags.SkipRenderers }); + }) + .OnMauiContextCreated((mauiContext) => + { + // This is the final Init that sets up the real context from the application. + + var state = new ActivationState(mauiContext); + Forms.Init(state); + }); + } + } +} diff --git a/src/Compatibility/Core/src/AppHostBuilderExtensions.cs b/src/Compatibility/Core/src/AppHostBuilderExtensions.cs index e4cb12600577..3c7a35fc1b0a 100644 --- a/src/Compatibility/Core/src/AppHostBuilderExtensions.cs +++ b/src/Compatibility/Core/src/AppHostBuilderExtensions.cs @@ -40,7 +40,7 @@ namespace Microsoft.Maui.Controls.Hosting { - public static class AppHostBuilderExtensions + public static partial class AppHostBuilderExtensions { public static IAppHostBuilder UseMauiApp(this IAppHostBuilder builder) where TApp : class, IApplication @@ -67,81 +67,11 @@ public static IAppHostBuilder UseMauiApp(this IAppHostBuilder builder, Fun return builder; } + static IAppHostBuilder SetupDefaults(this IAppHostBuilder builder) { - builder.ConfigureLifecycleEvents(events => - { -#if __ANDROID__ - events.AddAndroid(android => android - .OnApplicationCreating((app) => - { - // This is the initial Init to set up any system services registered by - // Forms.Init(). This happens in the Application's OnCreate - before - // any UI has appeared. - // This creates a dummy MauiContext that wraps the Application. - - var services = MauiApplication.Current.Services; - var mauiContext = new MauiContext(services, app); - var state = new ActivationState(mauiContext); - Forms.Init(state, new InitializationOptions { Flags = InitializationFlags.SkipRenderers }); - }) - .OnMauiContextCreated((mauiContext) => - { - // This is the final Init that sets up the real context from the activity. - - var state = new ActivationState(mauiContext); - Forms.Init(state); - })); -#elif __IOS__ - events.AddiOS(iOS => iOS - .WillFinishLaunching((app, options) => - { - // This is the initial Init to set up any system services registered by - // Forms.Init(). This happens before any UI has appeared. - // This creates a dummy MauiContext. - - var services = MauiUIApplicationDelegate.Current.Services; - var mauiContext = new MauiContext(services); - var state = new ActivationState(mauiContext); - Forms.Init(state, new InitializationOptions { Flags = InitializationFlags.SkipRenderers }); - return true; - }) - .OnMauiContextCreated((mauiContext) => - { - // This is the final Init that sets up the real context from the application. - - var state = new ActivationState(mauiContext); - Forms.Init(state); - })); -#elif WINDOWS - events.AddWindows(windows => windows - .OnLaunching((app, args) => - { - // This is the initial Init to set up any system services registered by - // Forms.Init(). This happens before any UI has appeared. - // This creates a dummy MauiContext. - // We need to call this so the Window and Root Page can new up successfully - // The dispatcher that's inside of Forms.Init needs to be setup before the initial - // window and root page start creating. - // Inside OnLaunched we grab the MauiContext that's on the window so we can have the correct - // MauiContext inside Forms - - var services = MauiWinUIApplication.Current.Services; - var mauiContext = new MauiContext(services); - var state = new ActivationState(mauiContext, args); - Forms.Init(state, new InitializationOptions { Flags = InitializationFlags.SkipRenderers }); - }) - .OnMauiContextCreated((mauiContext) => - { - // This is the final Init that sets up the real context from the application. - - var state = new ActivationState(mauiContext); - Forms.Init(state); - })); -#endif - }); - + builder.ConfigureCompatibilityLifecycleEvents(); builder .ConfigureMauiHandlers(handlers => { diff --git a/src/Compatibility/Core/src/AppHostBuilderExtensions.iOS.cs b/src/Compatibility/Core/src/AppHostBuilderExtensions.iOS.cs new file mode 100644 index 000000000000..15aec1938fcc --- /dev/null +++ b/src/Compatibility/Core/src/AppHostBuilderExtensions.iOS.cs @@ -0,0 +1,35 @@ +using Microsoft.Maui.Hosting; +using Microsoft.Maui.LifecycleEvents; +using Microsoft.Maui.Controls.Compatibility; + +namespace Microsoft.Maui.Controls.Hosting +{ + public static partial class AppHostBuilderExtensions + { + internal static IAppHostBuilder ConfigureCompatibilityLifecycleEvents(this IAppHostBuilder builder) => + builder.ConfigureLifecycleEvents(events => events.AddiOS(OnConfigureLifeCycle)); + + static void OnConfigureLifeCycle(IiOSLifecycleBuilder iOS) + { + iOS.WillFinishLaunching((app, options) => + { + // This is the initial Init to set up any system services registered by + // Forms.Init(). This happens before any UI has appeared. + // This creates a dummy MauiContext. + + var services = MauiUIApplicationDelegate.Current.Services; + var mauiContext = new MauiContext(services); + var state = new ActivationState(mauiContext); + Forms.Init(state, new InitializationOptions { Flags = InitializationFlags.SkipRenderers }); + return true; + }) + .OnMauiContextCreated((mauiContext) => + { + // This is the final Init that sets up the real context from the application. + + var state = new ActivationState(mauiContext); + Forms.Init(state); + }); + } + } +} diff --git a/src/Compatibility/Core/src/Windows/Forms.cs b/src/Compatibility/Core/src/Windows/Forms.cs index 343510412dfc..f38abffba096 100644 --- a/src/Compatibility/Core/src/Windows/Forms.cs +++ b/src/Compatibility/Core/src/Windows/Forms.cs @@ -119,11 +119,11 @@ public static void Init(IActivationState state, InitializationOptions? options = { MainWindow = mainWindow; - if (mainWindow is WindowsBasePage windowsPage) - { - windowsPage.LoadApplication(windowsPage.CreateApplication()); - windowsPage.Activate(); - } + //if (mainWindow is WindowsBasePage windowsPage) + //{ + // windowsPage.LoadApplication(windowsPage.CreateApplication()); + // windowsPage.Activate(); + //} } IsInitialized = true; diff --git a/src/Compatibility/Core/src/Windows/NavigationPageRenderer.cs b/src/Compatibility/Core/src/Windows/NavigationPageRenderer.cs index 794c271001af..4d6103f23e04 100644 --- a/src/Compatibility/Core/src/Windows/NavigationPageRenderer.cs +++ b/src/Compatibility/Core/src/Windows/NavigationPageRenderer.cs @@ -35,11 +35,8 @@ public class NavigationPageRenderer : IVisualElementRenderer, ITitleProvider, IT WImageSource _titleIcon; VisualElementTracker _tracker; EntranceThemeTransition _transition; - Platform _platform; bool _parentsLookedUp = false; - Platform Platform => _platform ?? (_platform = Platform.Current); - public NavigationPage Element { get; private set; } protected VisualElementTracker Tracker @@ -676,7 +673,7 @@ void UpdateBackButton() _container.SetBackButtonTitle(Element); } - async void UpdateTitleOnParents() + void UpdateTitleOnParents() { if (Element == null || _currentPage == null) return; @@ -710,10 +707,10 @@ async void UpdateTitleOnParents() if (_showTitle || (render != null && render.ShowTitle)) { - if (Platform != null) - { - await Platform.UpdateToolbarItems(); - } + //if (Platform != null) + //{ + // await Platform.UpdateToolbarItems(); + //} } } } diff --git a/src/Compatibility/Core/src/Windows/Platform.cs b/src/Compatibility/Core/src/Windows/Platform.cs index f711bdd9b1be..e1f60cfc1efc 100644 --- a/src/Compatibility/Core/src/Windows/Platform.cs +++ b/src/Compatibility/Core/src/Windows/Platform.cs @@ -109,15 +109,15 @@ public static IVisualElementRenderer CreateRenderer(VisualElement element) return renderer; } - internal static Platform Current - { - get - { - var frame = UI.Xaml.Window.Current?.Content as Microsoft.UI.Xaml.Controls.Frame; - var wbp = frame?.Content as WindowsBasePage; - return wbp?.Platform; - } - } + //internal static Platform Current + //{ + // get + // { + // var frame = UI.Xaml.Window.Current?.Content as Microsoft.UI.Xaml.Controls.Frame; + // var wbp = frame?.Content as WindowsBasePage; + // return wbp?.Platform; + // } + //} internal Platform(Microsoft.UI.Xaml.Window page) { diff --git a/src/Compatibility/Core/src/Windows/WindowsBasePage.cs b/src/Compatibility/Core/src/Windows/WindowsBasePage.cs index b73a3eb9de33..8762115bda99 100644 --- a/src/Compatibility/Core/src/Windows/WindowsBasePage.cs +++ b/src/Compatibility/Core/src/Windows/WindowsBasePage.cs @@ -1,83 +1,83 @@ -using System; -using System.ComponentModel; -using Windows.ApplicationModel; +//using System; +//using System.ComponentModel; +//using Windows.ApplicationModel; -namespace Microsoft.Maui.Controls.Compatibility.Platform.UWP -{ - public abstract class WindowsBasePage : Microsoft.UI.Xaml.Window - { +//namespace Microsoft.Maui.Controls.Compatibility.Platform.UWP +//{ +// public abstract class WindowsBasePage : Microsoft.UI.Xaml.Window +// { - Application _application; +// Application _application; - public WindowsBasePage() - { - if (!Windows.ApplicationModel.DesignMode.DesignModeEnabled) - { +// public WindowsBasePage() +// { +// if (!Windows.ApplicationModel.DesignMode.DesignModeEnabled) +// { - // TODO WINUI - // Microsoft.UI.Xaml.Application.Current.Suspending += OnApplicationSuspending; - // Microsoft.UI.Xaml.Application.Current.Resuming += OnApplicationResuming; - } - } +// // TODO WINUI +// // Microsoft.UI.Xaml.Application.Current.Suspending += OnApplicationSuspending; +// // Microsoft.UI.Xaml.Application.Current.Resuming += OnApplicationResuming; +// } +// } - internal Platform Platform { get; private set; } +// internal Platform Platform { get; private set; } - public abstract Application CreateApplication(); - protected abstract Platform CreatePlatform(); +// public abstract Application CreateApplication(); +// protected abstract Platform CreatePlatform(); - public virtual void LoadApplication(Application application) - { - if (application == null) - throw new ArgumentNullException("application"); +// public virtual void LoadApplication(Application application) +// { +// if (application == null) +// throw new ArgumentNullException("application"); - _application = application; - Application.SetCurrentApplication(application); - if (_application.MainPage != null) - RegisterWindow(_application.MainPage); - application.PropertyChanged += OnApplicationPropertyChanged; +// _application = application; +// Application.SetCurrentApplication(application); +// if (_application.MainPage != null) +// RegisterWindow(_application.MainPage); +// application.PropertyChanged += OnApplicationPropertyChanged; - _application.SendStart(); - } +// _application.SendStart(); +// } - protected void RegisterWindow(Page page) - { - if (page == null) - throw new ArgumentNullException("page"); +// protected void RegisterWindow(Page page) +// { +// if (page == null) +// throw new ArgumentNullException("page"); - Platform = CreatePlatform(); - Platform.SetPage(page); - } +// Platform = CreatePlatform(); +// Platform.SetPage(page); +// } - void OnApplicationPropertyChanged(object sender, PropertyChangedEventArgs e) - { - if (e.PropertyName == "MainPage") - { - if (Platform == null) - RegisterWindow(_application.MainPage); - Platform.SetPage(_application.MainPage); - } - } +// void OnApplicationPropertyChanged(object sender, PropertyChangedEventArgs e) +// { +// if (e.PropertyName == "MainPage") +// { +// if (Platform == null) +// RegisterWindow(_application.MainPage); +// Platform.SetPage(_application.MainPage); +// } +// } - void OnApplicationResuming(object sender, object e) - { - Application.Current?.SendResume(); - } +// void OnApplicationResuming(object sender, object e) +// { +// Application.Current?.SendResume(); +// } - async void OnApplicationSuspending(object sender, SuspendingEventArgs e) - { - var sendSleepTask = Application.Current?.SendSleepAsync(); - if (sendSleepTask == null) - return; +// async void OnApplicationSuspending(object sender, SuspendingEventArgs e) +// { +// var sendSleepTask = Application.Current?.SendSleepAsync(); +// if (sendSleepTask == null) +// return; - SuspendingDeferral deferral = e.SuspendingOperation.GetDeferral(); - try - { - await sendSleepTask; - } - finally - { - deferral.Complete(); - } - } - } -} \ No newline at end of file +// SuspendingDeferral deferral = e.SuspendingOperation.GetDeferral(); +// try +// { +// await sendSleepTask; +// } +// finally +// { +// deferral.Complete(); +// } +// } +// } +//} \ No newline at end of file diff --git a/src/Compatibility/Core/src/Windows/WindowsPage.cs b/src/Compatibility/Core/src/Windows/WindowsPage.cs deleted file mode 100644 index 3045a04850c7..000000000000 --- a/src/Compatibility/Core/src/Windows/WindowsPage.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Microsoft.Maui.Controls.Compatibility.Platform.UWP -{ - public abstract class WindowsPage : WindowsBasePage - { - protected override Platform CreatePlatform() - { - return new WindowsPlatform(this); - } - } -} \ No newline at end of file diff --git a/src/Controls/src/Core/Application.cs b/src/Controls/src/Core/Application.cs index 865ef19e301b..864a310fe80f 100644 --- a/src/Controls/src/Core/Application.cs +++ b/src/Controls/src/Core/Application.cs @@ -362,29 +362,25 @@ public void SendOnAppLinkRequestReceived(Uri uri) OnAppLinkRequestReceived(uri); } - [EditorBrowsable(EditorBrowsableState.Never)] - public void SendResume() + internal void SendResume() { Current = this; OnResume(); } - [EditorBrowsable(EditorBrowsableState.Never)] - public void SendSleep() + internal void SendSleep() { OnSleep(); SavePropertiesAsFireAndForget(); } - [EditorBrowsable(EditorBrowsableState.Never)] - public Task SendSleepAsync() + internal Task SendSleepAsync() { OnSleep(); return SavePropertiesAsync(); } - [EditorBrowsable(EditorBrowsableState.Never)] - public void SendStart() + internal void SendStart() { OnStart(); } diff --git a/src/Controls/src/Core/HandlerImpl/Window.Impl.cs b/src/Controls/src/Core/HandlerImpl/Window.Impl.cs index 6a45827c73ca..6bb01ef1f11d 100644 --- a/src/Controls/src/Core/HandlerImpl/Window.Impl.cs +++ b/src/Controls/src/Core/HandlerImpl/Window.Impl.cs @@ -5,7 +5,6 @@ using System.Collections.Specialized; using System.Runtime.CompilerServices; using System.Threading.Tasks; -using Microsoft.Maui.Animations; using Microsoft.Maui.Controls.Internals; using Microsoft.Maui.Controls.Platform; @@ -28,7 +27,6 @@ public Window() AlertManager = new AlertManager(this); ModalNavigationManager = new ModalNavigationManager(this); Navigation = new NavigationImpl(this); - InternalChildren.CollectionChanged += OnCollectionChanged; } @@ -51,15 +49,25 @@ public Window(Page page) } public event EventHandler? ModalPopped; - public event EventHandler? ModalPopping; - public event EventHandler? ModalPushed; - public event EventHandler? ModalPushing; - public event EventHandler? PopCanceled; + public event EventHandler? Created; + public event EventHandler? Resumed; + public event EventHandler? Activated; + public event EventHandler? Deactivated; + public event EventHandler? Stopped; + public event EventHandler? Destroying; + + protected virtual void OnCreated() { } + protected virtual void OnResumed() { } + protected virtual void OnActivated() { } + protected virtual void OnDeactivated() { } + protected virtual void OnStopped() { } + protected virtual void OnDestroying() { } + protected override void OnPropertyChanged([CallerMemberName] string? propertyName = null) { base.OnPropertyChanged(propertyName); @@ -83,6 +91,8 @@ protected override void OnPropertyChanged([CallerMemberName] string? propertyNam IView IWindow.Content => Page ?? throw new InvalidOperationException("No page was set on the window."); + Application? Application => Parent as Application; + void OnCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e) { if (e.OldItems != null) @@ -109,6 +119,84 @@ void OnCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e) } } + void SendWindowAppearing() + { + Page?.SendAppearing(); + } + + void OnModalPopped(Page modalPage) + { + var args = new ModalPoppedEventArgs(modalPage); + ModalPopped?.Invoke(this, args); + Application?.NotifyOfWindowModalEvent(args); + } + + bool OnModalPopping(Page modalPage) + { + var args = new ModalPoppingEventArgs(modalPage); + ModalPopping?.Invoke(this, args); + Application?.NotifyOfWindowModalEvent(args); + return args.Cancel; + } + + void OnModalPushed(Page modalPage) + { + var args = new ModalPushedEventArgs(modalPage); + ModalPushed?.Invoke(this, args); + Application?.NotifyOfWindowModalEvent(args); + } + + void OnModalPushing(Page modalPage) + { + var args = new ModalPushingEventArgs(modalPage); + ModalPushing?.Invoke(this, args); + Application?.NotifyOfWindowModalEvent(args); + } + + void OnPopCanceled() + { + PopCanceled?.Invoke(this, EventArgs.Empty); + } + + void IWindow.Created() + { + Created?.Invoke(this, EventArgs.Empty); + OnCreated(); + } + + void IWindow.Activated() + { + Activated?.Invoke(this, EventArgs.Empty); + OnActivated(); + Application?.SendResume(); + } + + void IWindow.Deactivated() + { + Deactivated?.Invoke(this, EventArgs.Empty); + OnDeactivated(); + } + + void IWindow.Stopped() + { + Stopped?.Invoke(this, EventArgs.Empty); + OnStopped(); + Application?.SendSleep(); + } + + void IWindow.Destroying() + { + Destroying?.Invoke(this, EventArgs.Empty); + OnDestroying(); + } + + void IWindow.Resumed() + { + Resumed?.Invoke(this, EventArgs.Empty); + OnResumed(); + } + + static void OnPageChanged(BindableObject bindable, object oldValue, object newValue) { if (bindable is not Window window) @@ -152,45 +240,6 @@ void OnPageHandlerChanging(object? sender, HandlerChangingEventArgs e) } } - void SendWindowAppearing() - { - Page?.SendAppearing(); - } - - void OnModalPopped(Page modalPage) - { - var args = new ModalPoppedEventArgs(modalPage); - ModalPopped?.Invoke(this, args); - (Parent as Application)?.NotifyOfWindowModalEvent(args); - } - - bool OnModalPopping(Page modalPage) - { - var args = new ModalPoppingEventArgs(modalPage); - ModalPopping?.Invoke(this, args); - (Parent as Application)?.NotifyOfWindowModalEvent(args); - return args.Cancel; - } - - void OnModalPushed(Page modalPage) - { - var args = new ModalPushedEventArgs(modalPage); - ModalPushed?.Invoke(this, args); - (Parent as Application)?.NotifyOfWindowModalEvent(args); - } - - void OnModalPushing(Page modalPage) - { - var args = new ModalPushingEventArgs(modalPage); - ModalPushing?.Invoke(this, args); - (Parent as Application)?.NotifyOfWindowModalEvent(args); - } - - void OnPopCanceled() - { - PopCanceled?.Invoke(this, EventArgs.Empty); - } - class NavigationImpl : NavigationProxy { readonly Window _owner; @@ -241,4 +290,4 @@ protected override async Task OnPushModal(Page modal, bool animated) } } } -} \ No newline at end of file +} diff --git a/src/Controls/src/Core/Platform/GestureManager/GestureManager.iOS.cs b/src/Controls/src/Core/Platform/GestureManager/GestureManager.iOS.cs index 18fce719b006..a23c2def0e2f 100644 --- a/src/Controls/src/Core/Platform/GestureManager/GestureManager.iOS.cs +++ b/src/Controls/src/Core/Platform/GestureManager/GestureManager.iOS.cs @@ -311,12 +311,13 @@ Action CreateChildRecognizerHandler(WeakReference weakEv { if (weakRecognizer.Target is IPinchGestureController pinchGestureRecognizer && weakEventTracker.Target is GestureManager eventTracker && - eventTracker._handler?.VirtualView is View view) + eventTracker._handler?.VirtualView is View view && + UIApplication.SharedApplication.GetKeyWindow() is UIWindow window) { var oldScale = eventTracker._previousScale; var originPoint = r.LocationInView(null); #if __MOBILE__ - originPoint = UIApplication.SharedApplication.GetKeyWindow().ConvertPointToView(originPoint, eventTracker._nativeView); + originPoint = window.ConvertPointToView(originPoint, eventTracker._nativeView); #else originPoint = NSApplication.SharedApplication.KeyWindow.ContentView.ConvertPointToView(originPoint, eventTracker._handler.NativeView); #endif diff --git a/src/Controls/src/Core/Platform/iOS/UIApplicationExtensions.cs b/src/Controls/src/Core/Platform/iOS/UIApplicationExtensions.cs deleted file mode 100644 index fc8b2c6e522a..000000000000 --- a/src/Controls/src/Core/Platform/iOS/UIApplicationExtensions.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; -using UIKit; - -namespace Microsoft.Maui.Controls.Platform -{ - internal static class UIApplicationExtensions - { - public static UIWindow GetKeyWindow(this UIApplication application) - { -#if __MOBILE__ - var windows = application.Windows; - - for (int i = 0; i < windows.Length; i++) - { - var window = windows[i]; - if (window.IsKeyWindow) - return window; - } - - return null; -#else - return application.KeyWindow; -#endif - - } - } -} diff --git a/src/Core/src/Core/IWindow.cs b/src/Core/src/Core/IWindow.cs index 775c718c2a9b..49ac88b21ebd 100644 --- a/src/Core/src/Core/IWindow.cs +++ b/src/Core/src/Core/IWindow.cs @@ -14,5 +14,17 @@ public interface IWindow : IElement /// Gets or sets the title displayed in the Window. /// string? Title { get; } + + void Created(); + + void Resumed(); + + void Activated(); + + void Deactivated(); + + void Stopped(); + + void Destroying(); } } \ No newline at end of file diff --git a/src/Core/src/Hosting/AppHost.cs b/src/Core/src/Hosting/AppHost.cs index 4d62b80f2988..6dbd530e5b71 100644 --- a/src/Core/src/Hosting/AppHost.cs +++ b/src/Core/src/Hosting/AppHost.cs @@ -1,4 +1,5 @@ #nullable enable +using Microsoft.Maui.LifecycleEvents; namespace Microsoft.Maui.Hosting { @@ -12,6 +13,7 @@ public static IAppHostBuilder CreateDefaultBuilder() builder.ConfigureFonts(); builder.ConfigureImageSources(); builder.ConfigureAnimations(); + builder.ConfigureCrossPlatformLifecycleEvents(); return builder; } diff --git a/src/Core/src/Hosting/LifecycleEvents/AppHostBuilderExtensions.Android.cs b/src/Core/src/Hosting/LifecycleEvents/AppHostBuilderExtensions.Android.cs new file mode 100644 index 000000000000..cb53ae1b425f --- /dev/null +++ b/src/Core/src/Hosting/LifecycleEvents/AppHostBuilderExtensions.Android.cs @@ -0,0 +1,49 @@ +using Microsoft.Maui.Hosting; +using Microsoft.Maui.LifecycleEvents; +using System; + +namespace Microsoft.Maui.LifecycleEvents +{ + public static partial class AppHostBuilderExtensions + { + internal static IAppHostBuilder ConfigureCrossPlatformLifecycleEvents(this IAppHostBuilder builder) => + builder.ConfigureLifecycleEvents(events => events.AddAndroid(OnConfigureLifeCycle)); + + static void OnConfigureLifeCycle(IAndroidLifecycleBuilder android) + { + android + .OnPostCreate((activity, bundle) => + { + // OnCreate is only ever called once when the activity is initally created + activity.GetWindow().Created(); + }) + .OnRestart(activity => + { + // Restart only runs after "OnStop" and then an activity is brought back into the foreground + activity.GetWindow().Resumed(); + }) + .OnResume(activity => + { + // this is called right before an activity is running + // it's called after onstart or it's called after app is unpausing + activity.GetWindow().Activated(); + + }) + .OnPause(activity => + { + // app has been backgrounded and lost focus but still might be visible + // think dialog prompt on top of activity + activity.GetWindow().Deactivated(); + }) + .OnStop(activity => + { + // Activity is no longer visible + activity.GetWindow().Stopped(); + }) + .OnDestroy(activity => + { + activity.GetWindow().Destroying(); + }); + } + } +} diff --git a/src/Core/src/Hosting/LifecycleEvents/AppHostBuilderExtensions.Standard.cs b/src/Core/src/Hosting/LifecycleEvents/AppHostBuilderExtensions.Standard.cs new file mode 100644 index 000000000000..ad43adb1ef8a --- /dev/null +++ b/src/Core/src/Hosting/LifecycleEvents/AppHostBuilderExtensions.Standard.cs @@ -0,0 +1,12 @@ +using Microsoft.Maui.Hosting; +using Microsoft.Maui.LifecycleEvents; +using System; + +namespace Microsoft.Maui.LifecycleEvents +{ + public static partial class AppHostBuilderExtensions + { + internal static IAppHostBuilder ConfigureCrossPlatformLifecycleEvents(this IAppHostBuilder builder) => + builder; + } +} diff --git a/src/Core/src/Hosting/LifecycleEvents/AppHostBuilderExtensions.Windows.cs b/src/Core/src/Hosting/LifecycleEvents/AppHostBuilderExtensions.Windows.cs new file mode 100644 index 000000000000..023f13ee2f20 --- /dev/null +++ b/src/Core/src/Hosting/LifecycleEvents/AppHostBuilderExtensions.Windows.cs @@ -0,0 +1,47 @@ +using Microsoft.Maui.Hosting; +using Microsoft.Maui.LifecycleEvents; +using System; + +namespace Microsoft.Maui.LifecycleEvents +{ + public static partial class AppHostBuilderExtensions + { + internal static IAppHostBuilder ConfigureCrossPlatformLifecycleEvents(this IAppHostBuilder builder) => + builder.ConfigureLifecycleEvents(events => events.AddWindows(OnConfigureLifeCycle)); + + static void OnConfigureLifeCycle(IWindowsLifecycleBuilder windows) + { + windows + .OnWindowCreated(window => + { + window.GetWindow().Created(); + }) + .OnResumed(window => + { + window.GetWindow().Resumed(); + }) + .OnActivated((window, args) => + { + switch (args.WindowActivationState) + { + case UI.Xaml.WindowActivationState.CodeActivated: + case UI.Xaml.WindowActivationState.PointerActivated: + window.GetWindow().Activated(); + break; + case UI.Xaml.WindowActivationState.Deactivated: + window.GetWindow().Deactivated(); + break; + } + }) + .OnVisibilityChanged((window, args) => + { + if (!args.Visible) + window.GetWindow().Stopped(); + }) + .OnClosed((window, args) => + { + window.GetWindow().Destroying(); + }); + } + } +} diff --git a/src/Core/src/LifecycleEvents/AppHostBuilderExtensions.cs b/src/Core/src/Hosting/LifecycleEvents/AppHostBuilderExtensions.cs similarity index 100% rename from src/Core/src/LifecycleEvents/AppHostBuilderExtensions.cs rename to src/Core/src/Hosting/LifecycleEvents/AppHostBuilderExtensions.cs diff --git a/src/Core/src/Hosting/LifecycleEvents/AppHostBuilderExtensions.iOS.cs b/src/Core/src/Hosting/LifecycleEvents/AppHostBuilderExtensions.iOS.cs new file mode 100644 index 000000000000..e3387d4e67a8 --- /dev/null +++ b/src/Core/src/Hosting/LifecycleEvents/AppHostBuilderExtensions.iOS.cs @@ -0,0 +1,42 @@ +using Microsoft.Maui.Hosting; +using Microsoft.Maui.LifecycleEvents; +using System; + +namespace Microsoft.Maui.LifecycleEvents +{ + public static partial class AppHostBuilderExtensions + { + internal static IAppHostBuilder ConfigureCrossPlatformLifecycleEvents(this IAppHostBuilder builder) => + builder.ConfigureLifecycleEvents(events => events.AddiOS(OnConfigureLifeCycle)); + + static void OnConfigureLifeCycle(IiOSLifecycleBuilder iOS) + { + iOS + .FinishedLaunching((app, launchOptions) => + { + app.GetWindow().Created(); + return true; + }) + .WillEnterForeground(app => + { + app.GetWindow().Resumed(); + }) + .OnActivated(app => + { + app.GetWindow().Activated(); + }) + .OnResignActivation(app => + { + app.GetWindow().Deactivated(); + }) + .DidEnterBackground(app => + { + app.GetWindow().Stopped(); + }) + .WillTerminate(app => + { + app.GetWindow().Destroying(); + }); + } + } +} diff --git a/src/Core/src/LifecycleEvents/Windows/WindowsLifecycle.cs b/src/Core/src/LifecycleEvents/Windows/WindowsLifecycle.cs index 71f44e9e4260..02ab1a9200b7 100644 --- a/src/Core/src/LifecycleEvents/Windows/WindowsLifecycle.cs +++ b/src/Core/src/LifecycleEvents/Windows/WindowsLifecycle.cs @@ -8,8 +8,10 @@ public static class WindowsLifecycle public delegate void OnLaunching(UI.Xaml.Application application, UI.Xaml.LaunchActivatedEventArgs args); public delegate void OnVisibilityChanged(UI.Xaml.Window window, UI.Xaml.WindowVisibilityChangedEventArgs args); public delegate void OnNativeMessage(UI.Xaml.Window window, WindowsNativeMessageEventArgs args); + public delegate void OnWindowCreated(UI.Xaml.Window window); + public delegate void OnResumed(UI.Xaml.Window window); // Internal events internal delegate void OnMauiContextCreated(IMauiContext mauiContext); } -} \ No newline at end of file +} diff --git a/src/Core/src/LifecycleEvents/Windows/WindowsLifecycleBuilderExtensions.cs b/src/Core/src/LifecycleEvents/Windows/WindowsLifecycleBuilderExtensions.cs index c6d7a724bc40..a58379ddaf41 100644 --- a/src/Core/src/LifecycleEvents/Windows/WindowsLifecycleBuilderExtensions.cs +++ b/src/Core/src/LifecycleEvents/Windows/WindowsLifecycleBuilderExtensions.cs @@ -7,6 +7,8 @@ public static class WindowsLifecycleBuilderExtensions public static IWindowsLifecycleBuilder OnLaunching(this IWindowsLifecycleBuilder lifecycle, WindowsLifecycle.OnLaunching del) => lifecycle.OnEvent(del); public static IWindowsLifecycleBuilder OnLaunched(this IWindowsLifecycleBuilder lifecycle, WindowsLifecycle.OnLaunched del) => lifecycle.OnEvent(del); public static IWindowsLifecycleBuilder OnVisibilityChanged(this IWindowsLifecycleBuilder lifecycle, WindowsLifecycle.OnVisibilityChanged del) => lifecycle.OnEvent(del); + public static IWindowsLifecycleBuilder OnWindowCreated(this IWindowsLifecycleBuilder lifecycle, WindowsLifecycle.OnWindowCreated del) => lifecycle.OnEvent(del); + public static IWindowsLifecycleBuilder OnResumed(this IWindowsLifecycleBuilder lifecycle, WindowsLifecycle.OnResumed del) => lifecycle.OnEvent(del); internal static IWindowsLifecycleBuilder OnMauiContextCreated(this IWindowsLifecycleBuilder lifecycle, WindowsLifecycle.OnMauiContextCreated del) => lifecycle.OnEvent(del); } diff --git a/src/Core/src/Platform/Android/ContextExtensions.cs b/src/Core/src/Platform/Android/ContextExtensions.cs index 93695ddfe520..230cedee128c 100644 --- a/src/Core/src/Platform/Android/ContextExtensions.cs +++ b/src/Core/src/Platform/Android/ContextExtensions.cs @@ -232,5 +232,21 @@ public static int GetDrawableId(this Resources resources, string packageName, st return resources.GetIdentifier(title, "drawable", packageName); } + + public static IWindow GetWindow(this Context context) + { + var nativeWindow = context.GetActivity(); + + if (nativeWindow is MauiAppCompatActivity mac && mac.VirtualWindow != null) + return mac.VirtualWindow; + + foreach (var window in MauiApplication.Current.Application.Windows) + { + if (window?.Handler?.NativeView is AActivity win && win == nativeWindow) + return window; + } + + throw new InvalidOperationException("Window Not Found"); + } } } \ No newline at end of file diff --git a/src/Core/src/Platform/Android/MauiAppCompatActivity.cs b/src/Core/src/Platform/Android/MauiAppCompatActivity.cs index 0e639922eea7..1b96ea68496c 100644 --- a/src/Core/src/Platform/Android/MauiAppCompatActivity.cs +++ b/src/Core/src/Platform/Android/MauiAppCompatActivity.cs @@ -8,6 +8,18 @@ namespace Microsoft.Maui { public partial class MauiAppCompatActivity : AppCompatActivity { + WeakReference? _virtualWindow; + internal IWindow? VirtualWindow + { + get + { + IWindow? window = null; + _virtualWindow?.TryGetTarget(out window); + return window; + } + } + + // Override this if you want to handle the default Android behavior of restoring fragments on an application restart protected virtual bool AllowFragmentRestore => false; @@ -72,6 +84,7 @@ void CreateNativeWindow(Bundle? savedInstanceState = null) window = mauiApp.CreateWindow(state); } + _virtualWindow = new WeakReference(window); this.SetWindow(window, mauiContext); } } diff --git a/src/Core/src/Platform/Windows/MauiWinUIApplication.cs b/src/Core/src/Platform/Windows/MauiWinUIApplication.cs index 5b8db57162be..0bcdf5484b0a 100644 --- a/src/Core/src/Platform/Windows/MauiWinUIApplication.cs +++ b/src/Core/src/Platform/Windows/MauiWinUIApplication.cs @@ -16,9 +16,6 @@ public abstract class MauiWinUIApplication : UI.Xaml.Application { protected abstract IStartup OnCreateStartup(); - public virtual UI.Xaml.Window CreateWindow() => - new MauiWinUIWindow(); - protected override void OnLaunched(UI.Xaml.LaunchActivatedEventArgs args) { LaunchActivatedEventArgs = args; @@ -59,6 +56,7 @@ UI.Xaml.Window CreateNativeWindow(UI.Xaml.LaunchActivatedEventArgs? args = null) var window = Application.CreateWindow(activationState); winuiWndow.SetWindow(window, mauiContext); + Services.InvokeLifecycleEvents(del => del(winuiWndow)); return winuiWndow; } diff --git a/src/Core/src/Platform/Windows/MauiWinUIWindow.cs b/src/Core/src/Platform/Windows/MauiWinUIWindow.cs index 9aa1bfc9c476..4482ef93ac29 100644 --- a/src/Core/src/Platform/Windows/MauiWinUIWindow.cs +++ b/src/Core/src/Platform/Windows/MauiWinUIWindow.cs @@ -7,6 +7,7 @@ namespace Microsoft.Maui { public class MauiWinUIWindow : UI.Xaml.Window { + bool _enableResumeEvent; public MauiWinUIWindow() { NativeMessage += OnNativeMessage; @@ -27,6 +28,14 @@ protected virtual void OnNativeMessage(object? sender, WindowsNativeMessageEvent protected virtual void OnActivated(object sender, UI.Xaml.WindowActivatedEventArgs args) { + if (args.WindowActivationState != UI.Xaml.WindowActivationState.Deactivated) + { + if (_enableResumeEvent) + MauiWinUIApplication.Current.Services?.InvokeLifecycleEvents(del => del(this)); + else + _enableResumeEvent = true; + } + MauiWinUIApplication.Current.Services?.InvokeLifecycleEvents(del => del(this, args)); } @@ -39,6 +48,7 @@ protected virtual void OnVisibilityChanged(object sender, UI.Xaml.WindowVisibili { MauiWinUIApplication.Current.Services?.InvokeLifecycleEvents(del => del(this, args)); } + public event EventHandler NativeMessage; #region Native Window diff --git a/src/Core/src/Platform/Windows/WindowExtensions.cs b/src/Core/src/Platform/Windows/WindowExtensions.cs index 1536e7c2ad5e..ff8b7c9ff898 100644 --- a/src/Core/src/Platform/Windows/WindowExtensions.cs +++ b/src/Core/src/Platform/Windows/WindowExtensions.cs @@ -1,8 +1,21 @@ -namespace Microsoft.Maui +using System; + +namespace Microsoft.Maui { public static class WindowExtensions { public static void UpdateTitle(this UI.Xaml.Window nativeWindow, IWindow window) => nativeWindow.Title = window.Title; + + public static IWindow GetWindow(this UI.Xaml.Window nativeWindow) + { + foreach(var window in MauiWinUIApplication.Current.Application.Windows) + { + if (window?.Handler?.NativeView is UI.Xaml.Window win && win == nativeWindow) + return window; + } + + throw new InvalidOperationException("Window Not Found"); + } } } \ No newline at end of file diff --git a/src/Core/src/Platform/iOS/MauiUIApplicationDelegate.cs b/src/Core/src/Platform/iOS/MauiUIApplicationDelegate.cs index c2b778f13055..3bc9fc493059 100644 --- a/src/Core/src/Platform/iOS/MauiUIApplicationDelegate.cs +++ b/src/Core/src/Platform/iOS/MauiUIApplicationDelegate.cs @@ -16,6 +16,17 @@ public class MauiUIApplicationDelegate : MauiUIApplicationDelegate public abstract class MauiUIApplicationDelegate : UIApplicationDelegate, IUIApplicationDelegate { + WeakReference? _virtualWindow; + internal IWindow? VirtualWindow + { + get + { + IWindow? window = null; + _virtualWindow?.TryGetTarget(out window); + return window; + } + } + protected MauiUIApplicationDelegate() { Current = this; @@ -65,7 +76,7 @@ UIWindow CreateNativeWindow() var activationState = new ActivationState(mauiContext); var window = Application.CreateWindow(activationState); - + _virtualWindow = new WeakReference(window); uiWindow.SetWindow(window, mauiContext); return uiWindow; diff --git a/src/Core/src/Platform/iOS/UIApplicationExtensions.cs b/src/Core/src/Platform/iOS/UIApplicationExtensions.cs new file mode 100644 index 000000000000..e8d1211e3d8e --- /dev/null +++ b/src/Core/src/Platform/iOS/UIApplicationExtensions.cs @@ -0,0 +1,37 @@ +using System; +using UIKit; + +namespace Microsoft.Maui +{ + internal static class UIApplicationExtensions + { + public static UIWindow? GetKeyWindow(this UIApplication application) + { + var windows = application.Windows; + + for (int i = 0; i < windows.Length; i++) + { + var window = windows[i]; + if (window.IsKeyWindow) + return window; + } + + return null; + } + + public static IWindow GetWindow(this UIApplication application) + { + if (MauiUIApplicationDelegate.Current.VirtualWindow != null) + return MauiUIApplicationDelegate.Current.VirtualWindow; + + var nativeWindow = application.GetKeyWindow(); + foreach (var window in MauiUIApplicationDelegate.Current.Application.Windows) + { + if (window?.Handler?.NativeView is UIWindow win && win == nativeWindow) + return window; + } + + throw new InvalidOperationException("Window Not Found"); + } + } +} diff --git a/src/Core/src/TaskExtensions.cs b/src/Core/src/TaskExtensions.cs index c64b53c8ff35..04eb55b03729 100644 --- a/src/Core/src/TaskExtensions.cs +++ b/src/Core/src/TaskExtensions.cs @@ -8,7 +8,7 @@ namespace Microsoft.Maui { public static class TaskExtensions { - public static async void FireAndForget( + internal static async void FireAndForget( this Task task, Action? errorCallback = null, Action? finishedCallBack = null @@ -28,10 +28,10 @@ public static class TaskExtensions } } - public static void FireAndForget(this Task task, ILogger? logger, [CallerMemberName] string? callerName = null) => + internal static void FireAndForget(this Task task, ILogger? logger, [CallerMemberName] string? callerName = null) => task.FireAndForget(ex => Log(logger, ex, callerName)); - public static void FireAndForget(this Task task, T? viewHandler, [CallerMemberName] string? callerName = null) + internal static void FireAndForget(this Task task, T? viewHandler, [CallerMemberName] string? callerName = null) where T : IViewHandler { task.FireAndForget(ex => Log(viewHandler?.CreateLogger(), ex, callerName)); diff --git a/src/Core/tests/Benchmarks/Stubs/WindowStub.cs b/src/Core/tests/Benchmarks/Stubs/WindowStub.cs index de5624b2f67d..5fb9ee37dac1 100644 --- a/src/Core/tests/Benchmarks/Stubs/WindowStub.cs +++ b/src/Core/tests/Benchmarks/Stubs/WindowStub.cs @@ -7,5 +7,47 @@ public class WindowStub : StubBase, IWindow public IView Content { get; set; } public string Title { get; set; } + + public bool IsCreated { get; set; } + public bool IsActivated { get; set; } + public bool IsDeactivated { get; set; } + public bool IsDestroyed { get; set; } + public bool IsResumed { get; set; } + public bool IsStopped { get; set; } + + public void Activated() + { + IsActivated = true; + IsDeactivated = false; + } + + public void Created() + { + IsCreated = true; + } + + public void Deactivated() + { + IsActivated = false; + IsDeactivated = false; + } + + public void Destroying() + { + IsDestroyed = true; + IsCreated = false; + } + + public void Resumed() + { + IsResumed = true; + IsStopped = false; + } + + public void Stopped() + { + IsStopped = true; + IsResumed = false; + } } } \ No newline at end of file diff --git a/src/Core/tests/DeviceTests/Stubs/WindowStub.cs b/src/Core/tests/DeviceTests/Stubs/WindowStub.cs index e683c47c541b..3df8dd7037e4 100644 --- a/src/Core/tests/DeviceTests/Stubs/WindowStub.cs +++ b/src/Core/tests/DeviceTests/Stubs/WindowStub.cs @@ -5,5 +5,46 @@ public class WindowStub : StubBase, IWindow public IView Content { get; set; } public string Title { get; set; } + public bool IsCreated { get; set; } + public bool IsActivated { get; set; } + public bool IsDeactivated { get; set; } + public bool IsDestroyed { get; set; } + public bool IsResumed { get; set; } + public bool IsStopped { get; set; } + + public void Activated() + { + IsActivated = true; + IsDeactivated = false; + } + + public void Created() + { + IsCreated = true; + } + + public void Deactivated() + { + IsActivated = false; + IsDeactivated = false; + } + + public void Destroying() + { + IsDestroyed = true; + IsCreated = false; + } + + public void Resumed() + { + IsResumed = true; + IsStopped = false; + } + + public void Stopped() + { + IsStopped = true; + IsResumed = false; + } } } \ No newline at end of file