From 6997283b84dc9b0f38d16ab246ce9c4aff4c147d Mon Sep 17 00:00:00 2001 From: redth Date: Fri, 4 Oct 2024 10:47:25 -0400 Subject: [PATCH 1/6] Edge to Edge on Modals for .NET 9 --- .../MauiAppEdgeToEdge.csproj | 6 +- .../MauiAppEdgeToEdge/MauiProgram.cs | 47 +++- .../Platforms/Android/EdgeToEdgeExtensions.cs | 237 ++++++++++++++++++ .../Platforms/Android/MainActivity.cs | 10 - 4 files changed, 285 insertions(+), 15 deletions(-) create mode 100644 MauiAppEdgeToEdge/MauiAppEdgeToEdge/Platforms/Android/EdgeToEdgeExtensions.cs diff --git a/MauiAppEdgeToEdge/MauiAppEdgeToEdge/MauiAppEdgeToEdge.csproj b/MauiAppEdgeToEdge/MauiAppEdgeToEdge/MauiAppEdgeToEdge.csproj index 79fecfa..4351db3 100644 --- a/MauiAppEdgeToEdge/MauiAppEdgeToEdge/MauiAppEdgeToEdge.csproj +++ b/MauiAppEdgeToEdge/MauiAppEdgeToEdge/MauiAppEdgeToEdge.csproj @@ -1,7 +1,7 @@  - net8.0-android + net9.0-android Exe MauiAppEdgeToEdge @@ -24,8 +24,8 @@ - - + + diff --git a/MauiAppEdgeToEdge/MauiAppEdgeToEdge/MauiProgram.cs b/MauiAppEdgeToEdge/MauiAppEdgeToEdge/MauiProgram.cs index 3f58def..f3ea468 100644 --- a/MauiAppEdgeToEdge/MauiAppEdgeToEdge/MauiProgram.cs +++ b/MauiAppEdgeToEdge/MauiAppEdgeToEdge/MauiProgram.cs @@ -1,4 +1,9 @@ -namespace MauiAppEdgeToEdge +using AndroidX.Activity; +using AndroidX.Fragment.App; +using Microsoft.Maui.LifecycleEvents; +using Microsoft.Maui.Platform; + +namespace MauiAppEdgeToEdge { public static class MauiProgram { @@ -6,9 +11,47 @@ public static MauiApp CreateMauiApp() { var builder = MauiApp.CreateBuilder(); builder - .UseMauiApp(); + .UseMauiApp() + .ConfigureLifecycleEvents(lifecycleBuilder => + { +#if ANDROID + lifecycleBuilder.AddAndroid(androidLifecycleBuilder => + { + androidLifecycleBuilder.OnCreate((activity, savedInstance) => + { + if (activity is ComponentActivity componentActivity) + { + // Enable Edge to Edge for the activity + EdgeToEdge.Enable(componentActivity); + + // Also wire up a fragment lifecycle callback so we can enable edge to edge on fragments + componentActivity.GetFragmentManager()?.RegisterFragmentLifecycleCallbacks(new MyFragmentLifecycleCallbacks((fragmentManager, fragment) => + { + // Modals in MAUI in NET9 use DialogFragment + if (fragment is DialogFragment dialogFragment) + { + // Edge to Edge on the fragment's window + dialogFragment.Dialog!.Window!.EnableEdgeToEdge(dialogFragment.Dialog!.Window!.DecorView!.Resources!); + } + }), false); + } + }); + }); +#endif + }); return builder.Build(); } } } + +#if ANDROID +public class MyFragmentLifecycleCallbacks(Action onFragmentStarted) : FragmentManager.FragmentLifecycleCallbacks +{ + public override void OnFragmentStarted(FragmentManager fm, Fragment f) + { + onFragmentStarted?.Invoke(fm, f); + base.OnFragmentStarted(fm, f); + } +} +#endif \ No newline at end of file diff --git a/MauiAppEdgeToEdge/MauiAppEdgeToEdge/Platforms/Android/EdgeToEdgeExtensions.cs b/MauiAppEdgeToEdge/MauiAppEdgeToEdge/Platforms/Android/EdgeToEdgeExtensions.cs new file mode 100644 index 0000000..f7b9323 --- /dev/null +++ b/MauiAppEdgeToEdge/MauiAppEdgeToEdge/Platforms/Android/EdgeToEdgeExtensions.cs @@ -0,0 +1,237 @@ +using Android.Content.Res; +using Android.Views; +using AndroidX.Core.View; +using System; + +namespace MauiAppEdgeToEdge; + +internal static class ActivityExtensions +{ + static global::Android.Graphics.Color DefaultLightScrim = global::Android.Graphics.Color.Argb(0xe6, 0xFF, 0xFF, 0xFF); + static global::Android.Graphics.Color DefaultDarkScrim = global::Android.Graphics.Color.Argb(0x80, 0x1b, 0x1b, 0x1b); + + + public static void EnableEdgeToEdge(this global::Android.App.Activity activity) + => activity.EnableEdgeToEdge( + SystemBarStyle.Auto(global::Android.Graphics.Color.Transparent, global::Android.Graphics.Color.Transparent), + SystemBarStyle.Auto(DefaultLightScrim, DefaultDarkScrim)); + + public static void EnableEdgeToEdge(this global::Android.App.Activity activity, SystemBarStyle statusBarStyle, SystemBarStyle navigationBarStyle) + => activity.Window?.EnableEdgeToEdge(activity.Resources!, statusBarStyle, navigationBarStyle); + + public static void EnableEdgeToEdge(this global::Android.Views.Window window, global::Android.Content.Res.Resources resources) + => window.EnableEdgeToEdge(resources, + SystemBarStyle.Auto(global::Android.Graphics.Color.Transparent, global::Android.Graphics.Color.Transparent), + SystemBarStyle.Auto(DefaultLightScrim, DefaultDarkScrim)); + + + public static void EnableEdgeToEdge(this global::Android.Views.Window window, global::Android.Content.Res.Resources resources, SystemBarStyle statusBarStyle, SystemBarStyle navigationBarStyle) + { + var res = resources; + var view = window!.DecorView; + var statusBarIsDark = statusBarStyle.DetectDarkMode(res!); + var navigationBarIsDark = navigationBarStyle.DetectDarkMode(res!); + + IEdgeToEdge impl; + + if (OperatingSystem.IsAndroidVersionAtLeast(30)) + impl = new EdgeToEdge30(); + else if (OperatingSystem.IsAndroidVersionAtLeast(29)) + impl = new EdgeToEdgeApi29(); + else if (OperatingSystem.IsAndroidVersionAtLeast(28)) + impl = new EdgeToEdgeApi28(); + else if (OperatingSystem.IsAndroidVersionAtLeast(26)) + impl = new EdgeToEdgeApi26(); + else if (OperatingSystem.IsAndroidVersionAtLeast(23)) + impl = new EdgeToEdgeApi23(); + else if (OperatingSystem.IsAndroidVersionAtLeast(21)) + impl = new EdgeToEdgeApi21(); + else + impl = new EdgeToEdgeBase(); + + impl.Setup(statusBarStyle, navigationBarStyle, window, view, statusBarIsDark, navigationBarIsDark); + + impl.AdjustLayoutInDisplayCutoutMode(window!); + } +} + + +internal class SystemBarStyle +{ + public SystemBarStyle(global::Android.Graphics.Color lightScrim, global::Android.Graphics.Color darkScrim, UiMode nightMode, Func detectDarkMode) + { + LightScrim = lightScrim; + DarkScrim = darkScrim; + NightMode = nightMode; + DetectDarkMode = detectDarkMode; + } + + public static SystemBarStyle Auto(global::Android.Graphics.Color lightScrim, global::Android.Graphics.Color darkScrim) + => new SystemBarStyle(lightScrim, darkScrim, UiMode.NightUndefined, res => + (res.Configuration!.UiMode & UiMode.NightMask) == UiMode.NightYes); + + public static SystemBarStyle Dark(global::Android.Graphics.Color scrim) + => new SystemBarStyle(scrim, scrim, UiMode.NightYes, res => true); + + public static SystemBarStyle Light(global::Android.Graphics.Color scrim, global::Android.Graphics.Color darkScrim) + => new SystemBarStyle(scrim, darkScrim, UiMode.NightNo, res => false); + + public global::Android.Graphics.Color LightScrim { get; private set; } + public global::Android.Graphics.Color DarkScrim { get; private set; } + + public UiMode NightMode { get; private set; } + + public Func DetectDarkMode { get; private set; } + + public global::Android.Graphics.Color GetScrim(bool isDark) + { + return isDark ? DarkScrim : LightScrim; + } + + public global::Android.Graphics.Color GetScrimWithEnforcedContrast(bool isDark) + { + if (NightMode == UiMode.NightUndefined) + return global::Android.Graphics.Color.Transparent; + + if (isDark) + return DarkScrim; + + return LightScrim; + } +} + +internal interface IEdgeToEdge +{ + void Setup( + SystemBarStyle statusBarStyle, + SystemBarStyle navigationBarStyle, + global::Android.Views.Window window, + global::Android.Views.View view, + bool statusBarIsDark, + bool navigationBarIsDark) + { + // No edge to edge, pre SDK 21 + } + + void AdjustLayoutInDisplayCutoutMode(global::Android.Views.Window window) + { + // No display cutout before SDK 28 + } +} + +internal class EdgeToEdgeBase : IEdgeToEdge +{ + public virtual void Setup( + SystemBarStyle statusBarStyle, + SystemBarStyle navigationBarStyle, + global::Android.Views.Window window, + global::Android.Views.View view, + bool statusBarIsDark, + bool navigationBarIsDark) + { + // No edge to edge, pre SDK 21 + } + + public virtual void AdjustLayoutInDisplayCutoutMode(global::Android.Views.Window window) + { + // No display cutout before SDK 28 + } +} + +internal class EdgeToEdgeApi21 : EdgeToEdgeBase +{ + public override void Setup( + SystemBarStyle statusBarStyle, + SystemBarStyle navigationBarStyle, + global::Android.Views.Window window, + global::Android.Views.View view, + bool statusBarIsDark, + bool navigationBarIsDark) + { + WindowCompat.SetDecorFitsSystemWindows(window, false); + window.AddFlags(WindowManagerFlags.TranslucentStatus); + window.AddFlags(WindowManagerFlags.TranslucentNavigation); + } +} + +internal class EdgeToEdgeApi23 : EdgeToEdgeBase +{ + [System.Diagnostics.CodeAnalysis.SuppressMessage("Interoperability", "CA1422:Validate platform compatibility", Justification = "")] + public override void Setup( + SystemBarStyle statusBarStyle, + SystemBarStyle navigationBarStyle, + global::Android.Views.Window window, + global::Android.Views.View view, + bool statusBarIsDark, + bool navigationBarIsDark) + { + WindowCompat.SetDecorFitsSystemWindows(window, false); + window.SetStatusBarColor(statusBarStyle.GetScrim(statusBarIsDark)); + window.SetNavigationBarColor(navigationBarStyle.DarkScrim); + new WindowInsetsControllerCompat(window, view).AppearanceLightStatusBars = !statusBarIsDark; + } +} + +internal class EdgeToEdgeApi26 : EdgeToEdgeBase +{ + [System.Diagnostics.CodeAnalysis.SuppressMessage("Interoperability", "CA1422:Validate platform compatibility", Justification = "")] + public override void Setup( + SystemBarStyle statusBarStyle, + SystemBarStyle navigationBarStyle, + global::Android.Views.Window window, + global::Android.Views.View view, + bool statusBarIsDark, + bool navigationBarIsDark) + { + WindowCompat.SetDecorFitsSystemWindows(window, false); + window.SetStatusBarColor(statusBarStyle.GetScrim(statusBarIsDark)); + window.SetNavigationBarColor(navigationBarStyle.GetScrim(navigationBarIsDark)); + var c = new WindowInsetsControllerCompat(window, view); + c.AppearanceLightStatusBars = !statusBarIsDark; + c.AppearanceLightNavigationBars = !navigationBarIsDark; + } +} + +internal class EdgeToEdgeApi28 : EdgeToEdgeApi26 +{ + public override void AdjustLayoutInDisplayCutoutMode(global::Android.Views.Window window) + { + if (OperatingSystem.IsAndroidVersionAtLeast(28)) + window.Attributes!.LayoutInDisplayCutoutMode = LayoutInDisplayCutoutMode.ShortEdges; + } +} + +internal class EdgeToEdgeApi29 : EdgeToEdgeApi28 +{ + [System.Diagnostics.CodeAnalysis.SuppressMessage("Interoperability", "CA1422:Validate platform compatibility", Justification = "")] + public override void Setup( + SystemBarStyle statusBarStyle, + SystemBarStyle navigationBarStyle, + global::Android.Views.Window window, + global::Android.Views.View view, + bool statusBarIsDark, + bool navigationBarIsDark) + { + if (OperatingSystem.IsAndroidVersionAtLeast(29)) + { + WindowCompat.SetDecorFitsSystemWindows(window, false); + window.SetStatusBarColor(statusBarStyle.GetScrimWithEnforcedContrast(navigationBarIsDark)); + window.SetNavigationBarColor(navigationBarStyle.GetScrimWithEnforcedContrast(navigationBarIsDark)); + window.StatusBarContrastEnforced = false; + window.NavigationBarContrastEnforced = navigationBarStyle.NightMode == UiMode.NightUndefined; + + var c = new WindowInsetsControllerCompat(window, view); + c.AppearanceLightStatusBars = !statusBarIsDark; + c.AppearanceLightNavigationBars = !navigationBarIsDark; + } + } +} + +internal class EdgeToEdge30 : EdgeToEdgeApi29 +{ + public override void AdjustLayoutInDisplayCutoutMode(global::Android.Views.Window window) + { + if (OperatingSystem.IsAndroidVersionAtLeast(30)) + window.Attributes!.LayoutInDisplayCutoutMode = LayoutInDisplayCutoutMode.Always; + } +} \ No newline at end of file diff --git a/MauiAppEdgeToEdge/MauiAppEdgeToEdge/Platforms/Android/MainActivity.cs b/MauiAppEdgeToEdge/MauiAppEdgeToEdge/Platforms/Android/MainActivity.cs index 0305c42..7d1dd76 100644 --- a/MauiAppEdgeToEdge/MauiAppEdgeToEdge/Platforms/Android/MainActivity.cs +++ b/MauiAppEdgeToEdge/MauiAppEdgeToEdge/Platforms/Android/MainActivity.cs @@ -9,15 +9,5 @@ namespace MauiAppEdgeToEdge [Activity(Theme = "@style/Maui.SplashTheme", MainLauncher = true, LaunchMode = LaunchMode.SingleTop, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize | ConfigChanges.Density)] public class MainActivity : MauiAppCompatActivity { - protected override void OnCreate(Bundle? savedInstanceState) - { - base.OnCreate(savedInstanceState); - - EdgeToEdge.Enable(this); - - //WindowCompat.SetDecorFitsSystemWindows(Window, false); - //Window.SetStatusBarColor(global::Android.Graphics.Color.Transparent); - //Window.SetNavigationBarColor(global::Android.Graphics.Color.Transparent); - } } } From d5fdb8c6feeeaed3d653b4a0e63778f134fe8619 Mon Sep 17 00:00:00 2001 From: redth Date: Fri, 4 Oct 2024 12:24:59 -0400 Subject: [PATCH 2/6] Fix namespace --- .../MauiAppEdgeToEdge/MauiProgram.cs | 2 +- .../Platforms/Android/EdgeToEdgeExtensions.cs | 1 - .../Platforms/Android/MainActivity.cs | 16 +++++++++++++++- MauiAppEdgeToEdge/global.json | 7 +++++++ 4 files changed, 23 insertions(+), 3 deletions(-) create mode 100644 MauiAppEdgeToEdge/global.json diff --git a/MauiAppEdgeToEdge/MauiAppEdgeToEdge/MauiProgram.cs b/MauiAppEdgeToEdge/MauiAppEdgeToEdge/MauiProgram.cs index f3ea468..7c965b7 100644 --- a/MauiAppEdgeToEdge/MauiAppEdgeToEdge/MauiProgram.cs +++ b/MauiAppEdgeToEdge/MauiAppEdgeToEdge/MauiProgram.cs @@ -28,9 +28,9 @@ public static MauiApp CreateMauiApp() componentActivity.GetFragmentManager()?.RegisterFragmentLifecycleCallbacks(new MyFragmentLifecycleCallbacks((fragmentManager, fragment) => { // Modals in MAUI in NET9 use DialogFragment - if (fragment is DialogFragment dialogFragment) { // Edge to Edge on the fragment's window + if (fragment is AndroidX.Fragment.App.DialogFragment dialogFragment) dialogFragment.Dialog!.Window!.EnableEdgeToEdge(dialogFragment.Dialog!.Window!.DecorView!.Resources!); } }), false); diff --git a/MauiAppEdgeToEdge/MauiAppEdgeToEdge/Platforms/Android/EdgeToEdgeExtensions.cs b/MauiAppEdgeToEdge/MauiAppEdgeToEdge/Platforms/Android/EdgeToEdgeExtensions.cs index f7b9323..7f07b65 100644 --- a/MauiAppEdgeToEdge/MauiAppEdgeToEdge/Platforms/Android/EdgeToEdgeExtensions.cs +++ b/MauiAppEdgeToEdge/MauiAppEdgeToEdge/Platforms/Android/EdgeToEdgeExtensions.cs @@ -24,7 +24,6 @@ public static void EnableEdgeToEdge(this global::Android.Views.Window window, gl SystemBarStyle.Auto(global::Android.Graphics.Color.Transparent, global::Android.Graphics.Color.Transparent), SystemBarStyle.Auto(DefaultLightScrim, DefaultDarkScrim)); - public static void EnableEdgeToEdge(this global::Android.Views.Window window, global::Android.Content.Res.Resources resources, SystemBarStyle statusBarStyle, SystemBarStyle navigationBarStyle) { var res = resources; diff --git a/MauiAppEdgeToEdge/MauiAppEdgeToEdge/Platforms/Android/MainActivity.cs b/MauiAppEdgeToEdge/MauiAppEdgeToEdge/Platforms/Android/MainActivity.cs index 7d1dd76..9434589 100644 --- a/MauiAppEdgeToEdge/MauiAppEdgeToEdge/Platforms/Android/MainActivity.cs +++ b/MauiAppEdgeToEdge/MauiAppEdgeToEdge/Platforms/Android/MainActivity.cs @@ -3,11 +3,25 @@ using Android.OS; using AndroidX.Activity; using AndroidX.Core.View; +using AndroidX.Fragment.App; +using Google.Android.Material.AppBar; namespace MauiAppEdgeToEdge { [Activity(Theme = "@style/Maui.SplashTheme", MainLauncher = true, LaunchMode = LaunchMode.SingleTop, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize | ConfigChanges.Density)] public class MainActivity : MauiAppCompatActivity { - } + //protected override void OnCreate(Bundle? savedInstanceState) + //{ + // base.OnCreate(savedInstanceState); + + // var appBarLayout = FindViewById(Resource.Id.navigationlayout_appbar); + + // appBarLayout?.SetFitsSystemWindows(true); + + // var contentLayout = FindViewById(Resource.Id.navigationlayout_content); + + // contentLayout?.SetFitsSystemWindows(true); + //} + } } diff --git a/MauiAppEdgeToEdge/global.json b/MauiAppEdgeToEdge/global.json new file mode 100644 index 0000000..ab51b21 --- /dev/null +++ b/MauiAppEdgeToEdge/global.json @@ -0,0 +1,7 @@ +{ + "sdk": { + "version": "8.0.400", + "allowPrerelease": true, + "rollForward": "latestMajor" + } +} \ No newline at end of file From 2df63409da4c3409ef27022c18cf1c7cea88fd67 Mon Sep 17 00:00:00 2001 From: redth Date: Fri, 4 Oct 2024 12:26:06 -0400 Subject: [PATCH 3/6] Cleanup --- .../MauiAppEdgeToEdge/MauiProgram.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/MauiAppEdgeToEdge/MauiAppEdgeToEdge/MauiProgram.cs b/MauiAppEdgeToEdge/MauiAppEdgeToEdge/MauiProgram.cs index 7c965b7..2328585 100644 --- a/MauiAppEdgeToEdge/MauiAppEdgeToEdge/MauiProgram.cs +++ b/MauiAppEdgeToEdge/MauiAppEdgeToEdge/MauiProgram.cs @@ -1,5 +1,6 @@ -using AndroidX.Activity; -using AndroidX.Fragment.App; +#if ANDROID +using AndroidX.Activity; +#endif using Microsoft.Maui.LifecycleEvents; using Microsoft.Maui.Platform; @@ -27,18 +28,17 @@ public static MauiApp CreateMauiApp() // Also wire up a fragment lifecycle callback so we can enable edge to edge on fragments componentActivity.GetFragmentManager()?.RegisterFragmentLifecycleCallbacks(new MyFragmentLifecycleCallbacks((fragmentManager, fragment) => { + // Edge to Edge on the fragment's window // Modals in MAUI in NET9 use DialogFragment - { - // Edge to Edge on the fragment's window if (fragment is AndroidX.Fragment.App.DialogFragment dialogFragment) dialogFragment.Dialog!.Window!.EnableEdgeToEdge(dialogFragment.Dialog!.Window!.DecorView!.Resources!); - } + }), false); } }); }); #endif - }); + }); return builder.Build(); } @@ -46,9 +46,9 @@ public static MauiApp CreateMauiApp() } #if ANDROID -public class MyFragmentLifecycleCallbacks(Action onFragmentStarted) : FragmentManager.FragmentLifecycleCallbacks +public class MyFragmentLifecycleCallbacks(Action onFragmentStarted) : AndroidX.Fragment.App.FragmentManager.FragmentLifecycleCallbacks { - public override void OnFragmentStarted(FragmentManager fm, Fragment f) + public override void OnFragmentStarted(AndroidX.Fragment.App.FragmentManager fm, AndroidX.Fragment.App.Fragment f) { onFragmentStarted?.Invoke(fm, f); base.OnFragmentStarted(fm, f); From a0d5119fff4e29b37d475e7348eebaa643f878ac Mon Sep 17 00:00:00 2001 From: redth Date: Fri, 4 Oct 2024 12:29:57 -0400 Subject: [PATCH 4/6] Use androidx edge to edge on dialogfragment too --- .../MauiAppEdgeToEdge/MauiProgram.cs | 8 +- .../Platforms/Android/EdgeToEdgeExtensions.cs | 236 ------------------ 2 files changed, 5 insertions(+), 239 deletions(-) delete mode 100644 MauiAppEdgeToEdge/MauiAppEdgeToEdge/Platforms/Android/EdgeToEdgeExtensions.cs diff --git a/MauiAppEdgeToEdge/MauiAppEdgeToEdge/MauiProgram.cs b/MauiAppEdgeToEdge/MauiAppEdgeToEdge/MauiProgram.cs index 2328585..5858958 100644 --- a/MauiAppEdgeToEdge/MauiAppEdgeToEdge/MauiProgram.cs +++ b/MauiAppEdgeToEdge/MauiAppEdgeToEdge/MauiProgram.cs @@ -28,10 +28,12 @@ public static MauiApp CreateMauiApp() // Also wire up a fragment lifecycle callback so we can enable edge to edge on fragments componentActivity.GetFragmentManager()?.RegisterFragmentLifecycleCallbacks(new MyFragmentLifecycleCallbacks((fragmentManager, fragment) => { - // Edge to Edge on the fragment's window // Modals in MAUI in NET9 use DialogFragment - if (fragment is AndroidX.Fragment.App.DialogFragment dialogFragment) - dialogFragment.Dialog!.Window!.EnableEdgeToEdge(dialogFragment.Dialog!.Window!.DecorView!.Resources!); + if (fragment is AndroidX.Fragment.App.DialogFragment dialogFragment && dialogFragment.Activity is not null) + { + // Edge to Edge on the fragment's activity too + EdgeToEdge.Enable(dialogFragment.Activity); + } }), false); } diff --git a/MauiAppEdgeToEdge/MauiAppEdgeToEdge/Platforms/Android/EdgeToEdgeExtensions.cs b/MauiAppEdgeToEdge/MauiAppEdgeToEdge/Platforms/Android/EdgeToEdgeExtensions.cs deleted file mode 100644 index 7f07b65..0000000 --- a/MauiAppEdgeToEdge/MauiAppEdgeToEdge/Platforms/Android/EdgeToEdgeExtensions.cs +++ /dev/null @@ -1,236 +0,0 @@ -using Android.Content.Res; -using Android.Views; -using AndroidX.Core.View; -using System; - -namespace MauiAppEdgeToEdge; - -internal static class ActivityExtensions -{ - static global::Android.Graphics.Color DefaultLightScrim = global::Android.Graphics.Color.Argb(0xe6, 0xFF, 0xFF, 0xFF); - static global::Android.Graphics.Color DefaultDarkScrim = global::Android.Graphics.Color.Argb(0x80, 0x1b, 0x1b, 0x1b); - - - public static void EnableEdgeToEdge(this global::Android.App.Activity activity) - => activity.EnableEdgeToEdge( - SystemBarStyle.Auto(global::Android.Graphics.Color.Transparent, global::Android.Graphics.Color.Transparent), - SystemBarStyle.Auto(DefaultLightScrim, DefaultDarkScrim)); - - public static void EnableEdgeToEdge(this global::Android.App.Activity activity, SystemBarStyle statusBarStyle, SystemBarStyle navigationBarStyle) - => activity.Window?.EnableEdgeToEdge(activity.Resources!, statusBarStyle, navigationBarStyle); - - public static void EnableEdgeToEdge(this global::Android.Views.Window window, global::Android.Content.Res.Resources resources) - => window.EnableEdgeToEdge(resources, - SystemBarStyle.Auto(global::Android.Graphics.Color.Transparent, global::Android.Graphics.Color.Transparent), - SystemBarStyle.Auto(DefaultLightScrim, DefaultDarkScrim)); - - public static void EnableEdgeToEdge(this global::Android.Views.Window window, global::Android.Content.Res.Resources resources, SystemBarStyle statusBarStyle, SystemBarStyle navigationBarStyle) - { - var res = resources; - var view = window!.DecorView; - var statusBarIsDark = statusBarStyle.DetectDarkMode(res!); - var navigationBarIsDark = navigationBarStyle.DetectDarkMode(res!); - - IEdgeToEdge impl; - - if (OperatingSystem.IsAndroidVersionAtLeast(30)) - impl = new EdgeToEdge30(); - else if (OperatingSystem.IsAndroidVersionAtLeast(29)) - impl = new EdgeToEdgeApi29(); - else if (OperatingSystem.IsAndroidVersionAtLeast(28)) - impl = new EdgeToEdgeApi28(); - else if (OperatingSystem.IsAndroidVersionAtLeast(26)) - impl = new EdgeToEdgeApi26(); - else if (OperatingSystem.IsAndroidVersionAtLeast(23)) - impl = new EdgeToEdgeApi23(); - else if (OperatingSystem.IsAndroidVersionAtLeast(21)) - impl = new EdgeToEdgeApi21(); - else - impl = new EdgeToEdgeBase(); - - impl.Setup(statusBarStyle, navigationBarStyle, window, view, statusBarIsDark, navigationBarIsDark); - - impl.AdjustLayoutInDisplayCutoutMode(window!); - } -} - - -internal class SystemBarStyle -{ - public SystemBarStyle(global::Android.Graphics.Color lightScrim, global::Android.Graphics.Color darkScrim, UiMode nightMode, Func detectDarkMode) - { - LightScrim = lightScrim; - DarkScrim = darkScrim; - NightMode = nightMode; - DetectDarkMode = detectDarkMode; - } - - public static SystemBarStyle Auto(global::Android.Graphics.Color lightScrim, global::Android.Graphics.Color darkScrim) - => new SystemBarStyle(lightScrim, darkScrim, UiMode.NightUndefined, res => - (res.Configuration!.UiMode & UiMode.NightMask) == UiMode.NightYes); - - public static SystemBarStyle Dark(global::Android.Graphics.Color scrim) - => new SystemBarStyle(scrim, scrim, UiMode.NightYes, res => true); - - public static SystemBarStyle Light(global::Android.Graphics.Color scrim, global::Android.Graphics.Color darkScrim) - => new SystemBarStyle(scrim, darkScrim, UiMode.NightNo, res => false); - - public global::Android.Graphics.Color LightScrim { get; private set; } - public global::Android.Graphics.Color DarkScrim { get; private set; } - - public UiMode NightMode { get; private set; } - - public Func DetectDarkMode { get; private set; } - - public global::Android.Graphics.Color GetScrim(bool isDark) - { - return isDark ? DarkScrim : LightScrim; - } - - public global::Android.Graphics.Color GetScrimWithEnforcedContrast(bool isDark) - { - if (NightMode == UiMode.NightUndefined) - return global::Android.Graphics.Color.Transparent; - - if (isDark) - return DarkScrim; - - return LightScrim; - } -} - -internal interface IEdgeToEdge -{ - void Setup( - SystemBarStyle statusBarStyle, - SystemBarStyle navigationBarStyle, - global::Android.Views.Window window, - global::Android.Views.View view, - bool statusBarIsDark, - bool navigationBarIsDark) - { - // No edge to edge, pre SDK 21 - } - - void AdjustLayoutInDisplayCutoutMode(global::Android.Views.Window window) - { - // No display cutout before SDK 28 - } -} - -internal class EdgeToEdgeBase : IEdgeToEdge -{ - public virtual void Setup( - SystemBarStyle statusBarStyle, - SystemBarStyle navigationBarStyle, - global::Android.Views.Window window, - global::Android.Views.View view, - bool statusBarIsDark, - bool navigationBarIsDark) - { - // No edge to edge, pre SDK 21 - } - - public virtual void AdjustLayoutInDisplayCutoutMode(global::Android.Views.Window window) - { - // No display cutout before SDK 28 - } -} - -internal class EdgeToEdgeApi21 : EdgeToEdgeBase -{ - public override void Setup( - SystemBarStyle statusBarStyle, - SystemBarStyle navigationBarStyle, - global::Android.Views.Window window, - global::Android.Views.View view, - bool statusBarIsDark, - bool navigationBarIsDark) - { - WindowCompat.SetDecorFitsSystemWindows(window, false); - window.AddFlags(WindowManagerFlags.TranslucentStatus); - window.AddFlags(WindowManagerFlags.TranslucentNavigation); - } -} - -internal class EdgeToEdgeApi23 : EdgeToEdgeBase -{ - [System.Diagnostics.CodeAnalysis.SuppressMessage("Interoperability", "CA1422:Validate platform compatibility", Justification = "")] - public override void Setup( - SystemBarStyle statusBarStyle, - SystemBarStyle navigationBarStyle, - global::Android.Views.Window window, - global::Android.Views.View view, - bool statusBarIsDark, - bool navigationBarIsDark) - { - WindowCompat.SetDecorFitsSystemWindows(window, false); - window.SetStatusBarColor(statusBarStyle.GetScrim(statusBarIsDark)); - window.SetNavigationBarColor(navigationBarStyle.DarkScrim); - new WindowInsetsControllerCompat(window, view).AppearanceLightStatusBars = !statusBarIsDark; - } -} - -internal class EdgeToEdgeApi26 : EdgeToEdgeBase -{ - [System.Diagnostics.CodeAnalysis.SuppressMessage("Interoperability", "CA1422:Validate platform compatibility", Justification = "")] - public override void Setup( - SystemBarStyle statusBarStyle, - SystemBarStyle navigationBarStyle, - global::Android.Views.Window window, - global::Android.Views.View view, - bool statusBarIsDark, - bool navigationBarIsDark) - { - WindowCompat.SetDecorFitsSystemWindows(window, false); - window.SetStatusBarColor(statusBarStyle.GetScrim(statusBarIsDark)); - window.SetNavigationBarColor(navigationBarStyle.GetScrim(navigationBarIsDark)); - var c = new WindowInsetsControllerCompat(window, view); - c.AppearanceLightStatusBars = !statusBarIsDark; - c.AppearanceLightNavigationBars = !navigationBarIsDark; - } -} - -internal class EdgeToEdgeApi28 : EdgeToEdgeApi26 -{ - public override void AdjustLayoutInDisplayCutoutMode(global::Android.Views.Window window) - { - if (OperatingSystem.IsAndroidVersionAtLeast(28)) - window.Attributes!.LayoutInDisplayCutoutMode = LayoutInDisplayCutoutMode.ShortEdges; - } -} - -internal class EdgeToEdgeApi29 : EdgeToEdgeApi28 -{ - [System.Diagnostics.CodeAnalysis.SuppressMessage("Interoperability", "CA1422:Validate platform compatibility", Justification = "")] - public override void Setup( - SystemBarStyle statusBarStyle, - SystemBarStyle navigationBarStyle, - global::Android.Views.Window window, - global::Android.Views.View view, - bool statusBarIsDark, - bool navigationBarIsDark) - { - if (OperatingSystem.IsAndroidVersionAtLeast(29)) - { - WindowCompat.SetDecorFitsSystemWindows(window, false); - window.SetStatusBarColor(statusBarStyle.GetScrimWithEnforcedContrast(navigationBarIsDark)); - window.SetNavigationBarColor(navigationBarStyle.GetScrimWithEnforcedContrast(navigationBarIsDark)); - window.StatusBarContrastEnforced = false; - window.NavigationBarContrastEnforced = navigationBarStyle.NightMode == UiMode.NightUndefined; - - var c = new WindowInsetsControllerCompat(window, view); - c.AppearanceLightStatusBars = !statusBarIsDark; - c.AppearanceLightNavigationBars = !navigationBarIsDark; - } - } -} - -internal class EdgeToEdge30 : EdgeToEdgeApi29 -{ - public override void AdjustLayoutInDisplayCutoutMode(global::Android.Views.Window window) - { - if (OperatingSystem.IsAndroidVersionAtLeast(30)) - window.Attributes!.LayoutInDisplayCutoutMode = LayoutInDisplayCutoutMode.Always; - } -} \ No newline at end of file From 6f926b19fdf1b613770e05c4c061a719bc151000 Mon Sep 17 00:00:00 2001 From: redth Date: Fri, 4 Oct 2024 12:42:15 -0400 Subject: [PATCH 5/6] Back to using custom edge to edge on dialog --- .../MauiAppEdgeToEdge/MauiProgram.cs | 11 +- .../Platforms/Android/EdgeToEdgeExtensions.cs | 236 ++++++++++++++++++ 2 files changed, 245 insertions(+), 2 deletions(-) create mode 100644 MauiAppEdgeToEdge/MauiAppEdgeToEdge/Platforms/Android/EdgeToEdgeExtensions.cs diff --git a/MauiAppEdgeToEdge/MauiAppEdgeToEdge/MauiProgram.cs b/MauiAppEdgeToEdge/MauiAppEdgeToEdge/MauiProgram.cs index 5858958..9e09a53 100644 --- a/MauiAppEdgeToEdge/MauiAppEdgeToEdge/MauiProgram.cs +++ b/MauiAppEdgeToEdge/MauiAppEdgeToEdge/MauiProgram.cs @@ -1,5 +1,8 @@ #if ANDROID +using Android.OS; using AndroidX.Activity; +using AndroidX.Fragment.App; + #endif using Microsoft.Maui.LifecycleEvents; using Microsoft.Maui.Platform; @@ -31,8 +34,12 @@ public static MauiApp CreateMauiApp() // Modals in MAUI in NET9 use DialogFragment if (fragment is AndroidX.Fragment.App.DialogFragment dialogFragment && dialogFragment.Activity is not null) { - // Edge to Edge on the fragment's activity too - EdgeToEdge.Enable(dialogFragment.Activity); + dialogFragment.Dialog?.Window?.EnableEdgeToEdge(dialogFragment.Dialog.Window.DecorView!.Resources!); + + // These don't seem to work, perhaps it's not getting the correct activity still + // if (dialogFragment.Dialog.OwnerActivity is ComponentActivity dfComponentActivity) + // EdgeToEdge.Enable(dfComponentActivity); + //EdgeToEdge.Enable(dialogFragment.Activity); } }), false); diff --git a/MauiAppEdgeToEdge/MauiAppEdgeToEdge/Platforms/Android/EdgeToEdgeExtensions.cs b/MauiAppEdgeToEdge/MauiAppEdgeToEdge/Platforms/Android/EdgeToEdgeExtensions.cs new file mode 100644 index 0000000..7f07b65 --- /dev/null +++ b/MauiAppEdgeToEdge/MauiAppEdgeToEdge/Platforms/Android/EdgeToEdgeExtensions.cs @@ -0,0 +1,236 @@ +using Android.Content.Res; +using Android.Views; +using AndroidX.Core.View; +using System; + +namespace MauiAppEdgeToEdge; + +internal static class ActivityExtensions +{ + static global::Android.Graphics.Color DefaultLightScrim = global::Android.Graphics.Color.Argb(0xe6, 0xFF, 0xFF, 0xFF); + static global::Android.Graphics.Color DefaultDarkScrim = global::Android.Graphics.Color.Argb(0x80, 0x1b, 0x1b, 0x1b); + + + public static void EnableEdgeToEdge(this global::Android.App.Activity activity) + => activity.EnableEdgeToEdge( + SystemBarStyle.Auto(global::Android.Graphics.Color.Transparent, global::Android.Graphics.Color.Transparent), + SystemBarStyle.Auto(DefaultLightScrim, DefaultDarkScrim)); + + public static void EnableEdgeToEdge(this global::Android.App.Activity activity, SystemBarStyle statusBarStyle, SystemBarStyle navigationBarStyle) + => activity.Window?.EnableEdgeToEdge(activity.Resources!, statusBarStyle, navigationBarStyle); + + public static void EnableEdgeToEdge(this global::Android.Views.Window window, global::Android.Content.Res.Resources resources) + => window.EnableEdgeToEdge(resources, + SystemBarStyle.Auto(global::Android.Graphics.Color.Transparent, global::Android.Graphics.Color.Transparent), + SystemBarStyle.Auto(DefaultLightScrim, DefaultDarkScrim)); + + public static void EnableEdgeToEdge(this global::Android.Views.Window window, global::Android.Content.Res.Resources resources, SystemBarStyle statusBarStyle, SystemBarStyle navigationBarStyle) + { + var res = resources; + var view = window!.DecorView; + var statusBarIsDark = statusBarStyle.DetectDarkMode(res!); + var navigationBarIsDark = navigationBarStyle.DetectDarkMode(res!); + + IEdgeToEdge impl; + + if (OperatingSystem.IsAndroidVersionAtLeast(30)) + impl = new EdgeToEdge30(); + else if (OperatingSystem.IsAndroidVersionAtLeast(29)) + impl = new EdgeToEdgeApi29(); + else if (OperatingSystem.IsAndroidVersionAtLeast(28)) + impl = new EdgeToEdgeApi28(); + else if (OperatingSystem.IsAndroidVersionAtLeast(26)) + impl = new EdgeToEdgeApi26(); + else if (OperatingSystem.IsAndroidVersionAtLeast(23)) + impl = new EdgeToEdgeApi23(); + else if (OperatingSystem.IsAndroidVersionAtLeast(21)) + impl = new EdgeToEdgeApi21(); + else + impl = new EdgeToEdgeBase(); + + impl.Setup(statusBarStyle, navigationBarStyle, window, view, statusBarIsDark, navigationBarIsDark); + + impl.AdjustLayoutInDisplayCutoutMode(window!); + } +} + + +internal class SystemBarStyle +{ + public SystemBarStyle(global::Android.Graphics.Color lightScrim, global::Android.Graphics.Color darkScrim, UiMode nightMode, Func detectDarkMode) + { + LightScrim = lightScrim; + DarkScrim = darkScrim; + NightMode = nightMode; + DetectDarkMode = detectDarkMode; + } + + public static SystemBarStyle Auto(global::Android.Graphics.Color lightScrim, global::Android.Graphics.Color darkScrim) + => new SystemBarStyle(lightScrim, darkScrim, UiMode.NightUndefined, res => + (res.Configuration!.UiMode & UiMode.NightMask) == UiMode.NightYes); + + public static SystemBarStyle Dark(global::Android.Graphics.Color scrim) + => new SystemBarStyle(scrim, scrim, UiMode.NightYes, res => true); + + public static SystemBarStyle Light(global::Android.Graphics.Color scrim, global::Android.Graphics.Color darkScrim) + => new SystemBarStyle(scrim, darkScrim, UiMode.NightNo, res => false); + + public global::Android.Graphics.Color LightScrim { get; private set; } + public global::Android.Graphics.Color DarkScrim { get; private set; } + + public UiMode NightMode { get; private set; } + + public Func DetectDarkMode { get; private set; } + + public global::Android.Graphics.Color GetScrim(bool isDark) + { + return isDark ? DarkScrim : LightScrim; + } + + public global::Android.Graphics.Color GetScrimWithEnforcedContrast(bool isDark) + { + if (NightMode == UiMode.NightUndefined) + return global::Android.Graphics.Color.Transparent; + + if (isDark) + return DarkScrim; + + return LightScrim; + } +} + +internal interface IEdgeToEdge +{ + void Setup( + SystemBarStyle statusBarStyle, + SystemBarStyle navigationBarStyle, + global::Android.Views.Window window, + global::Android.Views.View view, + bool statusBarIsDark, + bool navigationBarIsDark) + { + // No edge to edge, pre SDK 21 + } + + void AdjustLayoutInDisplayCutoutMode(global::Android.Views.Window window) + { + // No display cutout before SDK 28 + } +} + +internal class EdgeToEdgeBase : IEdgeToEdge +{ + public virtual void Setup( + SystemBarStyle statusBarStyle, + SystemBarStyle navigationBarStyle, + global::Android.Views.Window window, + global::Android.Views.View view, + bool statusBarIsDark, + bool navigationBarIsDark) + { + // No edge to edge, pre SDK 21 + } + + public virtual void AdjustLayoutInDisplayCutoutMode(global::Android.Views.Window window) + { + // No display cutout before SDK 28 + } +} + +internal class EdgeToEdgeApi21 : EdgeToEdgeBase +{ + public override void Setup( + SystemBarStyle statusBarStyle, + SystemBarStyle navigationBarStyle, + global::Android.Views.Window window, + global::Android.Views.View view, + bool statusBarIsDark, + bool navigationBarIsDark) + { + WindowCompat.SetDecorFitsSystemWindows(window, false); + window.AddFlags(WindowManagerFlags.TranslucentStatus); + window.AddFlags(WindowManagerFlags.TranslucentNavigation); + } +} + +internal class EdgeToEdgeApi23 : EdgeToEdgeBase +{ + [System.Diagnostics.CodeAnalysis.SuppressMessage("Interoperability", "CA1422:Validate platform compatibility", Justification = "")] + public override void Setup( + SystemBarStyle statusBarStyle, + SystemBarStyle navigationBarStyle, + global::Android.Views.Window window, + global::Android.Views.View view, + bool statusBarIsDark, + bool navigationBarIsDark) + { + WindowCompat.SetDecorFitsSystemWindows(window, false); + window.SetStatusBarColor(statusBarStyle.GetScrim(statusBarIsDark)); + window.SetNavigationBarColor(navigationBarStyle.DarkScrim); + new WindowInsetsControllerCompat(window, view).AppearanceLightStatusBars = !statusBarIsDark; + } +} + +internal class EdgeToEdgeApi26 : EdgeToEdgeBase +{ + [System.Diagnostics.CodeAnalysis.SuppressMessage("Interoperability", "CA1422:Validate platform compatibility", Justification = "")] + public override void Setup( + SystemBarStyle statusBarStyle, + SystemBarStyle navigationBarStyle, + global::Android.Views.Window window, + global::Android.Views.View view, + bool statusBarIsDark, + bool navigationBarIsDark) + { + WindowCompat.SetDecorFitsSystemWindows(window, false); + window.SetStatusBarColor(statusBarStyle.GetScrim(statusBarIsDark)); + window.SetNavigationBarColor(navigationBarStyle.GetScrim(navigationBarIsDark)); + var c = new WindowInsetsControllerCompat(window, view); + c.AppearanceLightStatusBars = !statusBarIsDark; + c.AppearanceLightNavigationBars = !navigationBarIsDark; + } +} + +internal class EdgeToEdgeApi28 : EdgeToEdgeApi26 +{ + public override void AdjustLayoutInDisplayCutoutMode(global::Android.Views.Window window) + { + if (OperatingSystem.IsAndroidVersionAtLeast(28)) + window.Attributes!.LayoutInDisplayCutoutMode = LayoutInDisplayCutoutMode.ShortEdges; + } +} + +internal class EdgeToEdgeApi29 : EdgeToEdgeApi28 +{ + [System.Diagnostics.CodeAnalysis.SuppressMessage("Interoperability", "CA1422:Validate platform compatibility", Justification = "")] + public override void Setup( + SystemBarStyle statusBarStyle, + SystemBarStyle navigationBarStyle, + global::Android.Views.Window window, + global::Android.Views.View view, + bool statusBarIsDark, + bool navigationBarIsDark) + { + if (OperatingSystem.IsAndroidVersionAtLeast(29)) + { + WindowCompat.SetDecorFitsSystemWindows(window, false); + window.SetStatusBarColor(statusBarStyle.GetScrimWithEnforcedContrast(navigationBarIsDark)); + window.SetNavigationBarColor(navigationBarStyle.GetScrimWithEnforcedContrast(navigationBarIsDark)); + window.StatusBarContrastEnforced = false; + window.NavigationBarContrastEnforced = navigationBarStyle.NightMode == UiMode.NightUndefined; + + var c = new WindowInsetsControllerCompat(window, view); + c.AppearanceLightStatusBars = !statusBarIsDark; + c.AppearanceLightNavigationBars = !navigationBarIsDark; + } + } +} + +internal class EdgeToEdge30 : EdgeToEdgeApi29 +{ + public override void AdjustLayoutInDisplayCutoutMode(global::Android.Views.Window window) + { + if (OperatingSystem.IsAndroidVersionAtLeast(30)) + window.Attributes!.LayoutInDisplayCutoutMode = LayoutInDisplayCutoutMode.Always; + } +} \ No newline at end of file From 605eb7cf1b86745f620ca7e2ecb965dd58b357d7 Mon Sep 17 00:00:00 2001 From: tranb3r Date: Tue, 15 Oct 2024 11:12:25 +0200 Subject: [PATCH 6/6] minor fix in edgetoedgeextensions --- .../MauiAppEdgeToEdge/Platforms/Android/EdgeToEdgeExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MauiAppEdgeToEdge/MauiAppEdgeToEdge/Platforms/Android/EdgeToEdgeExtensions.cs b/MauiAppEdgeToEdge/MauiAppEdgeToEdge/Platforms/Android/EdgeToEdgeExtensions.cs index 7f07b65..5fb3cc2 100644 --- a/MauiAppEdgeToEdge/MauiAppEdgeToEdge/Platforms/Android/EdgeToEdgeExtensions.cs +++ b/MauiAppEdgeToEdge/MauiAppEdgeToEdge/Platforms/Android/EdgeToEdgeExtensions.cs @@ -214,7 +214,7 @@ public override void Setup( if (OperatingSystem.IsAndroidVersionAtLeast(29)) { WindowCompat.SetDecorFitsSystemWindows(window, false); - window.SetStatusBarColor(statusBarStyle.GetScrimWithEnforcedContrast(navigationBarIsDark)); + window.SetStatusBarColor(statusBarStyle.GetScrimWithEnforcedContrast(statusBarIsDark)); window.SetNavigationBarColor(navigationBarStyle.GetScrimWithEnforcedContrast(navigationBarIsDark)); window.StatusBarContrastEnforced = false; window.NavigationBarContrastEnforced = navigationBarStyle.NightMode == UiMode.NightUndefined;