Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 3 additions & 3 deletions MauiAppEdgeToEdge/MauiAppEdgeToEdge/MauiAppEdgeToEdge.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>net8.0-android</TargetFrameworks>
<TargetFrameworks>net9.0-android</TargetFrameworks>

<OutputType>Exe</OutputType>
<RootNamespace>MauiAppEdgeToEdge</RootNamespace>
Expand All @@ -24,8 +24,8 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Maui.Controls" Version="8.0.90" />
<PackageReference Include="Microsoft.Maui.Controls.Compatibility" Version="8.0.90" />
<PackageReference Include="Microsoft.Maui.Controls" Version="$(MauiVersion)" />
<PackageReference Include="Microsoft.Maui.Controls.Compatibility" Version="$(MauiVersion)" />

<PackageReference Include="Xamarin.AndroidX.Activity" Version="1.9.2.1" />

Expand Down
56 changes: 54 additions & 2 deletions MauiAppEdgeToEdge/MauiAppEdgeToEdge/MauiProgram.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,66 @@
namespace MauiAppEdgeToEdge
#if ANDROID
using Android.OS;
using AndroidX.Activity;
using AndroidX.Fragment.App;

#endif
using Microsoft.Maui.LifecycleEvents;
using Microsoft.Maui.Platform;

namespace MauiAppEdgeToEdge
{
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>();
.UseMauiApp<App>()
.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 AndroidX.Fragment.App.DialogFragment dialogFragment && dialogFragment.Activity is not null)
{
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);
}
});
});
#endif
});

return builder.Build();
}
}
}

#if ANDROID
public class MyFragmentLifecycleCallbacks(Action<AndroidX.Fragment.App.FragmentManager, AndroidX.Fragment.App.Fragment> onFragmentStarted) : AndroidX.Fragment.App.FragmentManager.FragmentLifecycleCallbacks
{
public override void OnFragmentStarted(AndroidX.Fragment.App.FragmentManager fm, AndroidX.Fragment.App.Fragment f)
{
onFragmentStarted?.Invoke(fm, f);
base.OnFragmentStarted(fm, f);
}
}
#endif
Original file line number Diff line number Diff line change
@@ -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<global::Android.Content.Res.Resources, bool> 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<Resources, bool> 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 = "<Pending>")]
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 = "<Pending>")]
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 = "<Pending>")]
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(statusBarIsDark));
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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +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);
//protected override void OnCreate(Bundle? savedInstanceState)
//{
// base.OnCreate(savedInstanceState);

EdgeToEdge.Enable(this);
// var appBarLayout = FindViewById<AppBarLayout>(Resource.Id.navigationlayout_appbar);

//WindowCompat.SetDecorFitsSystemWindows(Window, false);
//Window.SetStatusBarColor(global::Android.Graphics.Color.Transparent);
//Window.SetNavigationBarColor(global::Android.Graphics.Color.Transparent);
}
}
// appBarLayout?.SetFitsSystemWindows(true);

// var contentLayout = FindViewById<FragmentContainerView>(Resource.Id.navigationlayout_content);

// contentLayout?.SetFitsSystemWindows(true);
//}
}
}
7 changes: 7 additions & 0 deletions MauiAppEdgeToEdge/global.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"sdk": {
"version": "8.0.400",
"allowPrerelease": true,
"rollForward": "latestMajor"
}
}