diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 92ab2b83bd..df98d2f6eb 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -8,6 +8,8 @@ Having that said, if you are a first-timer and you could use some help please re Furthermore, for anyone, we would like you to take into consideration the following guidelines. +## Kindness + ### Make an effort to be nice If you disagree, that's fine. We don't think about everything the same way, be respectful and at some point decide to agree to disagree. If a decision needs to be made, try to involve at least one other person without continuing an endless discussion. @@ -20,13 +22,13 @@ During a code review try to make a habit out of it to say at least one nice thin Remember English is not everyones native language. Written communication always lacks non-verbal communication. With written communication in a language that is not your native tongue it is even harder to express certain emotions. -Always assume that people mean to do right. Try to read a sentence a couple of times over and take things more literal. Try to place yourself in their shoes and see the message beyond the actual words. +Always assume that people mean to do right. Try to read a sentence a couple of times over and take things more literal. Try to place yourself in their shoes and see the message beyond the actual words. Things might come across different than they were intended, please keep that in mind and always check to see how someone meant it. If you're not sure, pull someone offline in a private channel on Twitter or email and chat about it for a bit. Maybe even jump on a call to collaborate. We're living in the 21st century, all the tools are there, why not use them to get to know each other and be friends?! Besides language, we understand that contributing to open-source mostly happens in your spare time. Remember that priorities might change and we can only spend our time once. This works as a two-way street: don't expect things to be solved instantly, but also please let us know if you do not have the capacity to finish work you have in progress. There is no shame in that. That way it's clear to other people that they can step in and take over. -### THANK YOU! +### THANK YOU Lastly, a big thank you for spending your precious time on our project. We appreciate any effort you make to help us with this project. @@ -45,7 +47,7 @@ We will take appropriate actions and measures if necessary. 1. Install latest stable [.NET SDK](https://dotnet.microsoft.com/en-us/download) 1. Install .NET MAUI workloads (we recommend using Visual Studio installer) -> You will need to complete a Contribution License Agreement before any pull request can be accepted. Complete the CLA at https://cla.dotnetfoundation.org/. This will also be triggered whenever you open a PR and the link should guide you through it. +> You will need to complete a Contribution License Agreement before any pull request can be accepted. Complete the CLA at . This will also be triggered whenever you open a PR and the link should guide you through it. ## Reporting a bug @@ -59,10 +61,10 @@ We always request a reproduction sample, and that's not to make your life hard o And most important: **Please, help us to help you ❤️** - ## Opening a PR process ### TL;DR + * Find an issue/feature, make sure that the issue/feature has been `Approved` and is welcomed (also see [New Features](https://github.com/CommunityToolkit/Maui#submitting-a-new-feature)) * Fork repository * Create branch @@ -73,13 +75,16 @@ And most important: **Please, help us to help you ❤️** ### Please consider -#### Tabs vs. Spaces?! +#### Tabs vs. Spaces? + [Tabs](https://www.reddit.com/r/javascript/comments/c8drjo/nobody_talks_about_the_real_reason_to_use_tabs/). #### Make your changes small, don't keep adding + We love your enthusiasm, but small changes and small PRs are easier to digest. We're all doing this in our spare time, it is easier to review a couple of small things and merge that and iterate from there than to have a PR with 100+ files changed that will sit there forever. #### Added features should have tests, a sample and documentation + We like quality as much as the next person, so please provide tests. In addition, we would want a new feature or change to be as clear as possible for other developers. Please add a sample to the sample app as part of your PR and also provide a PR to our [documentation repository](https://github.com/MicrosoftDocs/CommunityToolkit). @@ -88,67 +93,75 @@ In addition, we would want a new feature or change to be as clear as possible fo If you are unsure on where to locate the changes you need to make then please use the following section and flowchart. -![](https://user-images.githubusercontent.com/13558917/145694198-7addbd35-0e5f-4816-b351-759a01ec2672.png) +![New Code Workflow](https://user-images.githubusercontent.com/13558917/145694198-7addbd35-0e5f-4816-b351-759a01ec2672.png) ### CommunityToolkit.Maui.Core In general, this project will have all the basement to develop our Toolkit, including some primitive types, interfaces and base classes, base views, and common code. This will be referenced by other Frameworks/Toolkit based on .NET MAUI that wants to have the same features that us. -Here we will have some: +We ask that all classes in `CommunityToolkit.Maui.Core` are `public`. This allows developers to extend `CommunityToolkit.Maui.Core`. The `[EditorBrowsable(EditorBrowsableState.Never)]` attribute can be added to classes that we don't recommend developers discovering. -- BaseViews, could be Views that will be used by other Views, like PaddingButton (that's used by Snackbar) or the MauiPopup (used by Popup) that will be a native control implemented in a way that can work with our handler. This same approach is used here +Here is an example of classes that will -- Primitives, which will be base types that can be used by everyone, like our MathOperator. So other frameworks may not have the concept of Behavior or Converter but they can mimic them as helper classes/methods and use our primitives. +* BaseViews, could be Views that will be used by other Views, like PaddingButton (that's used by Snackbar) or the MauiPopup (used by Popup) that will be a native control implemented in a way that can work with our handler. This same approach is used here -- Common Code, this will be all generic code (platform-specific or not) that can be used by other Frameworks/Toolkits +* Primitives, which will be base types that can be used by everyone, like our MathOperator. So other frameworks may not have the concept of Behavior or Converter but they can mimic them as helper classes/methods and use our primitives. -- Layout Managers, were introduced on .NET MAUI and they live on Microsoft.Maui.Core so makes sense to have our managers on Core as well. +* Common Code, this will be all generic code (platform-specific or not) that can be used by other Frameworks/Toolkits -- Handlers, on Core will be the most general Handler with the majority of features. +* Layout Managers, were introduced on .NET MAUI and they live on Microsoft.Maui.Core so makes sense to have our managers on Core as well. -### CommunityToolkit.Maui: +* Handlers, on Core will be the most general Handler with the majority of features. + +### CommunityToolkit.Maui This project has a reference to the Core project. Here will live the implementation of our Controls, Views, Behaviors, Animations, etc. In other words, this project will work with the .NET MAUI and will be MVVM friendly. Also, other Toolkits/Frameworks can reference this package if needed. Here we will have some: -- View Implementation, with BindableProperties, support to attach effects, behaviors, triggers, and all that jazz. +* View Implementation, with BindableProperties, support to attach effects, behaviors, triggers, and all that jazz. -- Platform Configuration, that is Platform-specific features, that can relate to some control - like the ArrowDirection that is part of Popup and works just on iOS - or the application itself - like the StatusBarColorEffect from XCT. +* Platform Configuration, that is Platform-specific features, that can relate to some control - like the ArrowDirection that is part of Popup and works just on iOS - or the application itself - like the StatusBarColorEffect from XCT. -- Handlers Implementation, We will add to our PropertyMapper and/or CommandMapper any Platform Configuration that some Handler/View may have. We also can implement here some features that we think will not be great to have on Core. Here is a reference for this +* Handlers Implementation, We will add to our PropertyMapper and/or CommandMapper any Platform Configuration that some Handler/View may have. We also can implement here some features that we think will not be great to have on Core. Here is a reference for this -- Layout, will be the implementation of ours custom layouts and will use the Layout Managers on Core +* Layout, will be the implementation of ours custom layouts and will use the Layout Managers on Core ## Contributing Code - Best Practices ### Debug Logging + * Always use `Trace.WriteLine()` instead of `Debug.WriteLine` for debug logging because `Debug.WriteLine` is removed by the compiler in Release builds ### Methods Returning Task and ValueTask + * Always include a `CancellationToken` as a parameter to every method returning `Task` or `ValueTask` * If the method is public, provide a the default value for the `CancellationToken` (eg `CancellationToken token = default`) * If the method is not publc, do not provide a default value for the `CancellationToken` * Use `CancellationToken.ThrowIfCancellationRequested()` to verify the `CancellationToken` ### Enums + * Always use `Unknown` at index 0 for return types that may have a value that is not known * Always use `Default` at index 0 for option types that can use the system default option * Follow naming guidelines for tense... `SensorSpeed` not `SensorSpeeds` * Assign values (0,1,2,3) for all enums ### Property Names + * Include units only if one of the platforms includes it in their implementation. For instance HeadingMagneticNorth implies degrees on all platforms, but PressureInHectopascals is needed since platforms don't provide a consistent API for this. ### Units + * Use the standard units and most well accepted units when possible. For instance Hectopascals are used on UWP/Android and iOS uses Kilopascals so we have chosen Hectopascals. ### Pattern matching #### Null checking + * Prefer using `is` when checking for null instead of `==`. -e.g. +e.g. ```csharp // null @@ -180,9 +193,10 @@ if (something is Bucket bucket) ``` ### File Scoped Namespaces + * Use [file scoped namespaces](https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-10.0/file-scoped-namespaces) to help reduce code verbosity. -e.g. +e.g. ```csharp namespace CommunityToolkit.Maui.Converters; @@ -215,13 +229,13 @@ In other words, `NotImplementedException` implies that a feature is still in dev ### Bug Fixes -If you're looking for something to fix, please browse [open issues](https://github.com/CommunityToolkit/Maui/issues). +If you're looking for something to fix, please browse [open issues](https://github.com/CommunityToolkit/Maui/issues). Follow the style used by the [.NET Foundation](https://github.com/dotnet/runtime/blob/master/docs/coding-guidelines/coding-style.md), with two primary exceptions: -- We do not use the `private` keyword as it is the default accessibility level in C#. -- We will **not** use `_` or `s_` as a prefix for internal or private field names -- We will use `camelCaseFieldName` for naming internal or private fields in both instance and static implementations +* We do not use the `private` keyword as it is the default accessibility level in C#. +* We will **not** use `_` or `s_` as a prefix for internal or private field names +* We will use `camelCaseFieldName` for naming internal or private fields in both instance and static implementations Read and follow our [Pull Request template](https://github.com/CommunityToolkit/Maui/blob/main/.github/PULL_REQUEST_TEMPLATE.md) @@ -230,6 +244,7 @@ Read and follow our [Pull Request template](https://github.com/CommunityToolkit/ To propose a change or new feature, review the guidance on [Submitting a New Feature](https://github.com/CommunityToolkit/Maui#submitting-a-new-feature). #### Non-Starter Topics + The following topics should generally not be proposed for discussion as they are non-starters: * Large renames of APIs diff --git a/src/CommunityToolkit.Maui.Core/AppBuilderExtensions.shared.cs b/src/CommunityToolkit.Maui.Core/AppBuilderExtensions.shared.cs index 92de26908f..8b9c3464e3 100644 --- a/src/CommunityToolkit.Maui.Core/AppBuilderExtensions.shared.cs +++ b/src/CommunityToolkit.Maui.Core/AppBuilderExtensions.shared.cs @@ -1,4 +1,10 @@ -namespace CommunityToolkit.Maui.Core; +using System.Diagnostics; +using CommunityToolkit.Maui.Core.Services; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Maui.LifecycleEvents; +using Microsoft.Maui.Platform; + +namespace CommunityToolkit.Maui.Core; /// /// Extensions @@ -11,9 +17,44 @@ public static class AppBuilderExtensions /// generated by /// /// initialized for - public static MauiAppBuilder UseMauiCommunityToolkitCore(this MauiAppBuilder builder, Action? options = default) + public static MauiAppBuilder UseMauiCommunityToolkitCore(this MauiAppBuilder builder, Action? options = null) { options?.Invoke(new Options()); + +#if ANDROID + if (Options.ShouldUseStatusBarBehaviorOnAndroidModalPage) + { + builder.Services.AddSingleton(); + + builder.ConfigureLifecycleEvents(static lifecycleBuilder => + { + lifecycleBuilder.AddAndroid(static androidBuilder => + { + androidBuilder.OnCreate(static (activity, _) => + { + if (activity is not AndroidX.AppCompat.App.AppCompatActivity componentActivity) + { + Trace.WriteLine($"Unable to Modify Android StatusBar On ModalPage: Activity {activity.LocalClassName} must be an {nameof(AndroidX.AppCompat.App.AppCompatActivity)}"); + return; + } + + if (componentActivity.GetFragmentManager() is not AndroidX.Fragment.App.FragmentManager fragmentManager) + { + Trace.WriteLine($"Unable to Modify Android StatusBar On ModalPage: Unable to retrieve fragment manager from {nameof(AndroidX.AppCompat.App.AppCompatActivity)}"); + return; + } + + var dialogFragmentService = IPlatformApplication.Current?.Services.GetRequiredService() + ?? throw new InvalidOperationException($"Unable to retrieve {nameof(IDialogFragmentService)}"); + + + fragmentManager.RegisterFragmentLifecycleCallbacks(new FragmentLifecycleManager(dialogFragmentService), false); + }); + }); + }); + } +#endif + return builder; } } \ No newline at end of file diff --git a/src/CommunityToolkit.Maui.Core/Interfaces/IDialogFragmentService.android.cs b/src/CommunityToolkit.Maui.Core/Interfaces/IDialogFragmentService.android.cs new file mode 100644 index 0000000000..ccd3d4d6f9 --- /dev/null +++ b/src/CommunityToolkit.Maui.Core/Interfaces/IDialogFragmentService.android.cs @@ -0,0 +1,21 @@ +using Android.Content; +using Android.Views; + +namespace CommunityToolkit.Maui.Core; + +public interface IDialogFragmentService +{ + void OnFragmentAttached(AndroidX.Fragment.App.FragmentManager fm, AndroidX.Fragment.App.Fragment f, Context context); + void OnFragmentCreated(AndroidX.Fragment.App.FragmentManager fm, AndroidX.Fragment.App.Fragment f, Bundle? savedInstanceState); + void OnFragmentDestroyed(AndroidX.Fragment.App.FragmentManager fm, AndroidX.Fragment.App.Fragment f); + void OnFragmentDetached(AndroidX.Fragment.App.FragmentManager fm, AndroidX.Fragment.App.Fragment f); + void OnFragmentPaused(AndroidX.Fragment.App.FragmentManager fm, AndroidX.Fragment.App.Fragment f); + void OnFragmentPreAttached(AndroidX.Fragment.App.FragmentManager fm, AndroidX.Fragment.App.Fragment f, Context context); + void OnFragmentPreCreated(AndroidX.Fragment.App.FragmentManager fm, AndroidX.Fragment.App.Fragment f, Bundle? savedInstanceState); + void OnFragmentResumed(AndroidX.Fragment.App.FragmentManager fm, AndroidX.Fragment.App.Fragment f); + void OnFragmentSaveInstanceState(AndroidX.Fragment.App.FragmentManager fm, AndroidX.Fragment.App.Fragment f, Bundle outState); + void OnFragmentStarted(AndroidX.Fragment.App.FragmentManager fm, AndroidX.Fragment.App.Fragment f); + void OnFragmentStopped(AndroidX.Fragment.App.FragmentManager fm, AndroidX.Fragment.App.Fragment f); + void OnFragmentViewCreated(AndroidX.Fragment.App.FragmentManager fm, AndroidX.Fragment.App.Fragment f, View v, Bundle? savedInstanceState); + void OnFragmentViewDestroyed(AndroidX.Fragment.App.FragmentManager fm, AndroidX.Fragment.App.Fragment f); +} \ No newline at end of file diff --git a/src/CommunityToolkit.Maui.Core/Options.cs b/src/CommunityToolkit.Maui.Core/Options.cs index 7acaf46809..4f6e400f4c 100644 --- a/src/CommunityToolkit.Maui.Core/Options.cs +++ b/src/CommunityToolkit.Maui.Core/Options.cs @@ -5,4 +5,14 @@ namespace CommunityToolkit.Maui.Core; /// public class Options { + internal static bool ShouldUseStatusBarBehaviorOnAndroidModalPage { get; private set; } = true; + + /// + /// Enables the use of the DialogFragment Lifecycle service for Android. + /// + /// true if yes or false if you want to implement your own. + /// + /// Default value is true. + /// + public void SetShouldUseStatusBarBehaviorOnAndroidModalPage(bool value) => ShouldUseStatusBarBehaviorOnAndroidModalPage = value; } \ No newline at end of file diff --git a/src/CommunityToolkit.Maui.Core/Services/DialogFragmentService.android.cs b/src/CommunityToolkit.Maui.Core/Services/DialogFragmentService.android.cs new file mode 100644 index 0000000000..0aa2635f01 --- /dev/null +++ b/src/CommunityToolkit.Maui.Core/Services/DialogFragmentService.android.cs @@ -0,0 +1,163 @@ +using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; +using Android.Content; +using Android.Views; +using AndroidX.AppCompat.App; +using DialogFragment = AndroidX.Fragment.App.DialogFragment; +using Fragment = AndroidX.Fragment.App.Fragment; +using FragmentManager = AndroidX.Fragment.App.FragmentManager; + +namespace CommunityToolkit.Maui.Core.Services; + +sealed partial class DialogFragmentService : IDialogFragmentService +{ + [EditorBrowsable(EditorBrowsableState.Never)] + public void OnFragmentAttached(FragmentManager fm, Fragment f, Context context) + { + } + + [EditorBrowsable(EditorBrowsableState.Never)] + public void OnFragmentCreated(FragmentManager fm, Fragment f, Bundle? savedInstanceState) + { + } + + [EditorBrowsable(EditorBrowsableState.Never)] + public void OnFragmentDestroyed(FragmentManager fm, Fragment f) + { + } + + [EditorBrowsable(EditorBrowsableState.Never)] + public void OnFragmentDetached(FragmentManager fm, Fragment f) + { + } + + [EditorBrowsable(EditorBrowsableState.Never)] + public void OnFragmentPaused(FragmentManager fm, Fragment f) + { + } + + [EditorBrowsable(EditorBrowsableState.Never)] + public void OnFragmentPreAttached(FragmentManager fm, Fragment f, Context context) + { + } + + [EditorBrowsable(EditorBrowsableState.Never)] + public void OnFragmentPreCreated(FragmentManager fm, Fragment f, Bundle? savedInstanceState) + { + } + + [EditorBrowsable(EditorBrowsableState.Never)] + public void OnFragmentResumed(FragmentManager fm, Fragment f) + { + } + + [EditorBrowsable(EditorBrowsableState.Never)] + public void OnFragmentSaveInstanceState(FragmentManager fm, Fragment f, Bundle outState) + { + } + + [EditorBrowsable(EditorBrowsableState.Never)] + public void OnFragmentStarted(FragmentManager fm, Fragment f) + { + if (!TryConvertToDialogFragment(f, out var dialogFragment) || Microsoft.Maui.ApplicationModel.Platform.CurrentActivity is not AppCompatActivity activity) + { + return; + } + + HandleStatusBarColor(dialogFragment, activity); + } + + [EditorBrowsable(EditorBrowsableState.Never)] + public void OnFragmentStopped(FragmentManager fm, Fragment f) + { + } + + [EditorBrowsable(EditorBrowsableState.Never)] + public void OnFragmentViewCreated(FragmentManager fm, Fragment f, Android.Views.View v, Bundle? savedInstanceState) + { + } + + [EditorBrowsable(EditorBrowsableState.Never)] + public void OnFragmentViewDestroyed(FragmentManager fm, Fragment f) + { + } + + static bool TryConvertToDialogFragment(Fragment fragment, [NotNullWhen(true)] out DialogFragment? dialogFragment) + { + dialogFragment = null; + + if (fragment is not DialogFragment dialog) + { + return false; + } + + dialogFragment = dialog; + return true; + } + + static void HandleStatusBarColor(DialogFragment dialogFragment, AppCompatActivity activity) + { + if (activity.Window is null) + { + return; + } + + var statusBarColor = activity.Window.StatusBarColor; + var platformColor = new Android.Graphics.Color(statusBarColor); + if (dialogFragment.Dialog?.Window is not Window dialogWindow) + { + throw new InvalidOperationException("Dialog window cannot be null"); + } + + var isColorTransparent = platformColor == Android.Graphics.Color.Transparent; + + if (OperatingSystem.IsAndroidVersionAtLeast(30)) + { + var windowInsetsController = dialogWindow.InsetsController; + var appearance = activity.Window.InsetsController?.SystemBarsAppearance; + + if (windowInsetsController is null) + { + System.Diagnostics.Trace.WriteLine("WindowInsetsController is null, cannot set system bars appearance."); + return; + } + + if (appearance.HasValue) + { + windowInsetsController.SetSystemBarsAppearance(appearance.Value, appearance.Value); + } + else + { + windowInsetsController.SetSystemBarsAppearance( + isColorTransparent ? 0 : (int)WindowInsetsControllerAppearance.LightStatusBars, + (int)WindowInsetsControllerAppearance.LightStatusBars); + } + + dialogWindow.SetStatusBarColor(platformColor); + + if (!OperatingSystem.IsAndroidVersionAtLeast(35)) + { + dialogWindow.SetDecorFitsSystemWindows(!isColorTransparent); + } + else + { + AndroidX.Core.View.WindowCompat.SetDecorFitsSystemWindows(dialogWindow, !isColorTransparent); + } + } + else + { + dialogWindow.SetStatusBarColor(platformColor); + + if (isColorTransparent) + { + dialogWindow.ClearFlags(WindowManagerFlags.DrawsSystemBarBackgrounds); + dialogWindow.SetFlags(WindowManagerFlags.LayoutNoLimits, WindowManagerFlags.LayoutNoLimits); + } + else + { + dialogWindow.ClearFlags(WindowManagerFlags.LayoutNoLimits); + dialogWindow.SetFlags(WindowManagerFlags.DrawsSystemBarBackgrounds, WindowManagerFlags.DrawsSystemBarBackgrounds); + } + } + } +} \ No newline at end of file diff --git a/src/CommunityToolkit.Maui.Core/Services/DialogFragmentService.shared.cs b/src/CommunityToolkit.Maui.Core/Services/DialogFragmentService.shared.cs new file mode 100644 index 0000000000..fb168af753 --- /dev/null +++ b/src/CommunityToolkit.Maui.Core/Services/DialogFragmentService.shared.cs @@ -0,0 +1,14 @@ +using System.ComponentModel; +using System.Runtime.Versioning; + +namespace CommunityToolkit.Maui.Core.Services; + +/// +/// An Android-specific service to support on modal pages +/// +[EditorBrowsable(EditorBrowsableState.Never)] +[SupportedOSPlatform("Android")] +public sealed partial class DialogFragmentService +{ + +} \ No newline at end of file diff --git a/src/CommunityToolkit.Maui.Core/Services/FragmentLifecycleManager.android.cs b/src/CommunityToolkit.Maui.Core/Services/FragmentLifecycleManager.android.cs new file mode 100644 index 0000000000..6d68be885c --- /dev/null +++ b/src/CommunityToolkit.Maui.Core/Services/FragmentLifecycleManager.android.cs @@ -0,0 +1,89 @@ +using System.ComponentModel; +using Android.Content; +using FragmentManager = AndroidX.Fragment.App.FragmentManager; + +namespace CommunityToolkit.Maui.Core.Services; + +[EditorBrowsable(EditorBrowsableState.Never)] +public sealed class FragmentLifecycleManager(IDialogFragmentService dialogFragmentService) : FragmentManager.FragmentLifecycleCallbacks +{ + readonly IDialogFragmentService dialogFragmentService = dialogFragmentService; + + public override void OnFragmentAttached(FragmentManager fm, AndroidX.Fragment.App.Fragment f, Context context) + { + base.OnFragmentAttached(fm, f, context); + dialogFragmentService.OnFragmentAttached(fm, f, context); + } + + public override void OnFragmentCreated(FragmentManager fm, AndroidX.Fragment.App.Fragment f, Bundle? savedInstanceState) + { + base.OnFragmentCreated(fm, f, savedInstanceState); + dialogFragmentService.OnFragmentCreated(fm, f, savedInstanceState); + } + + public override void OnFragmentDestroyed(FragmentManager fm, AndroidX.Fragment.App.Fragment f) + { + base.OnFragmentDestroyed(fm, f); + dialogFragmentService.OnFragmentDestroyed(fm, f); + } + + public override void OnFragmentDetached(FragmentManager fm, AndroidX.Fragment.App.Fragment f) + { + base.OnFragmentDetached(fm, f); + dialogFragmentService.OnFragmentDetached(fm, f); + } + + public override void OnFragmentPaused(FragmentManager fm, AndroidX.Fragment.App.Fragment f) + { + base.OnFragmentPaused(fm, f); + dialogFragmentService.OnFragmentPaused(fm, f); + } + + public override void OnFragmentPreAttached(FragmentManager fm, AndroidX.Fragment.App.Fragment f, Context context) + { + base.OnFragmentPreAttached(fm, f, context); + dialogFragmentService.OnFragmentPreAttached(fm, f, context); + } + + public override void OnFragmentPreCreated(FragmentManager fm, AndroidX.Fragment.App.Fragment f, Bundle? savedInstanceState) + { + base.OnFragmentPreCreated(fm, f, savedInstanceState); + dialogFragmentService.OnFragmentPreCreated(fm, f, savedInstanceState); + } + + public override void OnFragmentResumed(FragmentManager fm, AndroidX.Fragment.App.Fragment f) + { + base.OnFragmentResumed(fm, f); + dialogFragmentService.OnFragmentResumed(fm, f); + } + + public override void OnFragmentSaveInstanceState(FragmentManager fm, AndroidX.Fragment.App.Fragment f, Bundle outState) + { + base.OnFragmentSaveInstanceState(fm, f, outState); + dialogFragmentService.OnFragmentSaveInstanceState(fm, f, outState); + } + + public override void OnFragmentStarted(FragmentManager fm, AndroidX.Fragment.App.Fragment f) + { + base.OnFragmentStarted(fm, f); + dialogFragmentService.OnFragmentStarted(fm, f); + } + + public override void OnFragmentStopped(FragmentManager fm, AndroidX.Fragment.App.Fragment f) + { + base.OnFragmentStopped(fm, f); + dialogFragmentService.OnFragmentStopped(fm, f); + } + + public override void OnFragmentViewCreated(FragmentManager fm, AndroidX.Fragment.App.Fragment f, Android.Views.View v, Bundle? savedInstanceState) + { + base.OnFragmentViewCreated(fm, f, v, savedInstanceState); + dialogFragmentService.OnFragmentViewCreated(fm, f, v, savedInstanceState); + } + + public override void OnFragmentViewDestroyed(FragmentManager fm, AndroidX.Fragment.App.Fragment f) + { + base.OnFragmentViewDestroyed(fm, f); + dialogFragmentService.OnFragmentViewDestroyed(fm, f); + } +} \ No newline at end of file diff --git a/src/CommunityToolkit.Maui/AppBuilderExtensions.shared.cs b/src/CommunityToolkit.Maui/AppBuilderExtensions.shared.cs index 404f8719c5..5e45d440ee 100644 --- a/src/CommunityToolkit.Maui/AppBuilderExtensions.shared.cs +++ b/src/CommunityToolkit.Maui/AppBuilderExtensions.shared.cs @@ -16,17 +16,17 @@ public static class AppBuilderExtensions /// generated by /// /// initialized for - public static MauiAppBuilder UseMauiCommunityToolkit(this MauiAppBuilder builder, Action? options = default) + public static MauiAppBuilder UseMauiCommunityToolkit(this MauiAppBuilder builder, Action? options = null) { // Pass `null` because `options?.Invoke()` will set options on both `CommunityToolkit.Maui` and `CommunityToolkit.Maui.Core` builder.UseMauiCommunityToolkitCore(null); - builder.Services.AddSingleton(); - // Invokes options for both `CommunityToolkit.Maui` and `CommunityToolkit.Maui.Core` options?.Invoke(new Options(builder)); - builder.ConfigureMauiHandlers(h => + builder.Services.AddSingleton(); + + builder.ConfigureMauiHandlers(static h => { h.AddHandler(); h.AddHandler();