Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix statusBar color changes on modal pages #2413

Merged
merged 28 commits into from
Feb 2, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
0630c5c
create services for DialogFragment
pictos Dec 26, 2024
0a7d0c8
register the service on android during the builder phase
pictos Dec 26, 2024
410fe31
move interface to Interfaces folder
pictos Dec 26, 2024
2364d10
move services to CommunityToolkit.Maui
pictos Dec 26, 2024
29aac74
add option to not use the default MCT impl
pictos Dec 26, 2024
1e2142f
move the registration bits into to CommunityToolkit.Maui project
pictos Dec 26, 2024
0e38c75
removed files
pictos Dec 26, 2024
b2fa42f
code cleanup
pictos Dec 26, 2024
03eb3cf
add trace
pictos Dec 26, 2024
d7a53c7
remove debug assert
pictos Dec 26, 2024
048a024
Update Naming
TheCodeTraveler Dec 27, 2024
6704cd7
Move to `CommunityToolkit.Maui.Core`
TheCodeTraveler Dec 27, 2024
5753c2d
`dotnet format`
TheCodeTraveler Dec 27, 2024
6e45f20
`dotnet format`
TheCodeTraveler Dec 27, 2024
4cdb611
Merge branch 'main' into pj/fix-status-bar-on-modal
TheCodeTraveler Jan 9, 2025
bcd13da
Merge branch 'main' into pj/fix-status-bar-on-modal
TheCodeTraveler Jan 10, 2025
c78e1ba
Promote to `public`
TheCodeTraveler Jan 10, 2025
d9145dd
Update CONTRIBUTING.md
TheCodeTraveler Jan 10, 2025
1775a81
Add XML Comments
TheCodeTraveler Jan 10, 2025
0462d85
Add `[EditorBrowsable(EditorBrowsableState.Never)]`
TheCodeTraveler Jan 10, 2025
b490d35
Merge branch 'main' into pj/fix-status-bar-on-modal
TheCodeTraveler Jan 10, 2025
1752606
Merge branch 'pj/fix-status-bar-on-modal' of https://github.com/Commu…
TheCodeTraveler Jan 10, 2025
6f4220c
Merge branch 'main' into pj/fix-status-bar-on-modal
TheCodeTraveler Jan 10, 2025
fd4c9c8
Merge branch 'main' into pj/fix-status-bar-on-modal
TheCodeTraveler Jan 13, 2025
6c85020
Merge branch 'main' into pj/fix-status-bar-on-modal
TheCodeTraveler Jan 16, 2025
1205dd6
Merge branch 'main' into pj/fix-status-bar-on-modal
jfversluis Jan 17, 2025
ae5a867
Merge branch 'main' into pj/fix-status-bar-on-modal
TheCodeTraveler Jan 29, 2025
b2e1019
Show it to the world
pictos Feb 1, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 43 additions & 2 deletions src/CommunityToolkit.Maui.Core/AppBuilderExtensions.shared.cs
Original file line number Diff line number Diff line change
@@ -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;

/// <summary>
/// <see cref="MauiAppBuilder"/> Extensions
Expand All @@ -11,9 +17,44 @@ public static class AppBuilderExtensions
/// <param name="builder"><see cref="MauiAppBuilder"/> generated by <see cref="MauiApp"/> </param>
/// <param name="options"><see cref="Options"/></param>
/// <returns><see cref="MauiAppBuilder"/> initialized for <see cref="CommunityToolkit.Maui.Core"/></returns>
public static MauiAppBuilder UseMauiCommunityToolkitCore(this MauiAppBuilder builder, Action<Options>? options = default)
public static MauiAppBuilder UseMauiCommunityToolkitCore(this MauiAppBuilder builder, Action<Options>? options = null)
{
options?.Invoke(new Options());

#if ANDROID
if (Options.ShouldUseStatusBarBehaviorOnAndroidModalPage)
{
builder.Services.AddSingleton<IDialogFragmentService, DialogFragmentService>();

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<IDialogFragmentService>()
?? throw new InvalidOperationException($"Unable to retrieve {nameof(IDialogFragmentService)}");


fragmentManager.RegisterFragmentLifecycleCallbacks(new FragmentLifecycleManager(dialogFragmentService), false);
});
});
});
}
#endif

return builder;
}
}
10 changes: 10 additions & 0 deletions src/CommunityToolkit.Maui.Core/Options.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,14 @@ namespace CommunityToolkit.Maui.Core;
/// </summary>
public class Options
{
internal static bool ShouldUseStatusBarBehaviorOnAndroidModalPage { get; private set; } = true;

/// <summary>
/// Enables the use of the DialogFragment Lifecycle service for Android.
/// </summary>
/// <param name="value">true if yes or false if you want to implement your own.</param>
/// <remarks>
/// Default value is true.
/// </remarks>
public void SetShouldUseStatusBarBehaviorOnAndroidModalPage(bool value) => ShouldUseStatusBarBehaviorOnAndroidModalPage = value;
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
using System.Diagnostics.CodeAnalysis;
using Android.Content;
using Android.OS;
using Android.Views;
using AndroidX.AppCompat.App;
using CommunityToolkit.Maui.Core;
using Debug = System.Diagnostics.Debug;
using DialogFragment = AndroidX.Fragment.App.DialogFragment;
using Fragment = AndroidX.Fragment.App.Fragment;
using FragmentManager = AndroidX.Fragment.App.FragmentManager;

namespace CommunityToolkit.Maui.Services;
namespace CommunityToolkit.Maui.Core.Services;

sealed partial class DialogFragmentService : IDialogFragmentService
{
Expand Down Expand Up @@ -51,7 +48,7 @@ public void OnFragmentSaveInstanceState(FragmentManager fm, Fragment f, Bundle o

public void OnFragmentStarted(FragmentManager fm, Fragment f)
{
if (!IsDialogFragment(f, out var dialogFragment) || Platform.CurrentActivity is not AppCompatActivity activity)
if (!TryConvertToDialogFragment(f, out var dialogFragment) || Microsoft.Maui.ApplicationModel.Platform.CurrentActivity is not AppCompatActivity activity)
{
return;
}
Expand All @@ -68,18 +65,16 @@ static void HandleStatusBarColor(DialogFragment dialogFragment, AppCompatActivit

var statusBarColor = activity.Window.StatusBarColor;
var platformColor = new Android.Graphics.Color(statusBarColor);
var dialog = dialogFragment.Dialog;

Debug.Assert(dialog is not null);
Debug.Assert(dialog.Window is not null);

var window = dialog.Window;
if (dialogFragment.Dialog?.Window is not Window dialogWindow)
{
throw new InvalidOperationException("Dialog window cannot be null");
TheCodeTraveler marked this conversation as resolved.
Show resolved Hide resolved
}

bool isColorTransparent = platformColor == Android.Graphics.Color.Transparent;
var isColorTransparent = platformColor == Android.Graphics.Color.Transparent;

if (OperatingSystem.IsAndroidVersionAtLeast(30))
{
var windowInsetsController = window.InsetsController;
var windowInsetsController = dialogWindow.InsetsController;
var appearance = activity.Window.InsetsController?.SystemBarsAppearance;

if (windowInsetsController is null)
Expand All @@ -98,29 +93,31 @@ static void HandleStatusBarColor(DialogFragment dialogFragment, AppCompatActivit
isColorTransparent ? 0 : (int)WindowInsetsControllerAppearance.LightStatusBars,
(int)WindowInsetsControllerAppearance.LightStatusBars);
}
window.SetStatusBarColor(platformColor);

dialogWindow.SetStatusBarColor(platformColor);

if (!OperatingSystem.IsAndroidVersionAtLeast(35))
{
window.SetDecorFitsSystemWindows(!isColorTransparent);
dialogWindow.SetDecorFitsSystemWindows(!isColorTransparent);
}
else
{
AndroidX.Core.View.WindowCompat.SetDecorFitsSystemWindows(window, !isColorTransparent);
AndroidX.Core.View.WindowCompat.SetDecorFitsSystemWindows(dialogWindow, !isColorTransparent);
}
}
else
{
dialog.Window.SetStatusBarColor(platformColor);
dialogWindow.SetStatusBarColor(platformColor);

if (isColorTransparent)
{
window.ClearFlags(WindowManagerFlags.DrawsSystemBarBackgrounds);
window.SetFlags(WindowManagerFlags.LayoutNoLimits, WindowManagerFlags.LayoutNoLimits);
dialogWindow.ClearFlags(WindowManagerFlags.DrawsSystemBarBackgrounds);
dialogWindow.SetFlags(WindowManagerFlags.LayoutNoLimits, WindowManagerFlags.LayoutNoLimits);
}
else
{
window.ClearFlags(WindowManagerFlags.LayoutNoLimits);
window.SetFlags(WindowManagerFlags.DrawsSystemBarBackgrounds, WindowManagerFlags.DrawsSystemBarBackgrounds);
dialogWindow.ClearFlags(WindowManagerFlags.LayoutNoLimits);
dialogWindow.SetFlags(WindowManagerFlags.DrawsSystemBarBackgrounds, WindowManagerFlags.DrawsSystemBarBackgrounds);
}
}
}
Expand All @@ -137,14 +134,16 @@ public void OnFragmentViewDestroyed(FragmentManager fm, Fragment f)
{
}

static bool IsDialogFragment(Fragment fragment, [NotNullWhen(true)] out DialogFragment? dialogFragment)
static bool TryConvertToDialogFragment(Fragment fragment, [NotNullWhen(true)] out DialogFragment? dialogFragment)
{
dialogFragment = null;
if (fragment is DialogFragment dialog)

if (fragment is not DialogFragment dialog)
{
dialogFragment = dialog;
return true;
return false;
}
return false;

dialogFragment = dialog;
return true;
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace CommunityToolkit.Maui.Services;
namespace CommunityToolkit.Maui.Core.Services;

sealed partial class DialogFragmentService
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
using Android.Content;
using Android.OS;
using CommunityToolkit.Maui.Core;
using FragmentManager = AndroidX.Fragment.App.FragmentManager;

namespace CommunityToolkit.Maui.Services;
namespace CommunityToolkit.Maui.Core.Services;

sealed class FragmentLifecycleManager(IDialogFragmentService dialogFragmentService) : FragmentManager.FragmentLifecycleCallbacks
{
Expand Down
35 changes: 0 additions & 35 deletions src/CommunityToolkit.Maui/AppBuilderExtensions.shared.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
using CommunityToolkit.Maui.Core;
using CommunityToolkit.Maui.Core.Handlers;
using CommunityToolkit.Maui.PlatformConfiguration.AndroidSpecific;
using CommunityToolkit.Maui.Services;
using CommunityToolkit.Maui.Views;
using Microsoft.Maui.LifecycleEvents;
using Microsoft.Maui.Platform;
Expand All @@ -28,40 +27,6 @@ public static MauiAppBuilder UseMauiCommunityToolkit(this MauiAppBuilder builder
// Invokes options for both `CommunityToolkit.Maui` and `CommunityToolkit.Maui.Core`
options?.Invoke(new Options(builder));

#if ANDROID
if (Options.ShouldUseStatusBarBehaviorOnAndroidModalPage)
{
builder.Services.AddSingleton<IDialogFragmentService, DialogFragmentService>();

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<IDialogFragmentService>()
?? throw new InvalidOperationException($"Unable to retrieve {nameof(IDialogFragmentService)}");


fragmentManager.RegisterFragmentLifecycleCallbacks(new FragmentLifecycleManager(dialogFragmentService), false);
});
});
});
}
#endif

builder.Services.AddSingleton<IPopupService, PopupService>();

builder.ConfigureMauiHandlers(static h =>
Expand Down
12 changes: 1 addition & 11 deletions src/CommunityToolkit.Maui/Options.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@ internal Options(in MauiAppBuilder builder) : this()
{
this.builder = builder;
}

internal static bool ShouldUseStatusBarBehaviorOnAndroidModalPage { get; private set; } = true;

internal static bool ShouldSuppressExceptionsInAnimations { get; private set; }
internal static bool ShouldSuppressExceptionsInConverters { get; private set; }
internal static bool ShouldSuppressExceptionsInBehaviors { get; private set; }
Expand Down Expand Up @@ -48,15 +47,6 @@ internal Options(in MauiAppBuilder builder) : this()
/// </remarks>
public void SetShouldSuppressExceptionsInBehaviors(bool value) => ShouldSuppressExceptionsInBehaviors = value;

/// <summary>
/// Enables the use of the DialogFragment Lifecycle service for Android.
/// </summary>
/// <param name="value">true if yes or false if you want to implement your own.</param>
/// <remarks>
/// Default value is true.
/// </remarks>
public void SetShouldUseStatusBarBehaviorOnAndroidModalPage(bool value) => ShouldUseStatusBarBehaviorOnAndroidModalPage = value;

/// <summary>
/// Enables <see cref="Alerts.Snackbar"/> for Windows
/// </summary>
Expand Down
Loading