Skip to content
Merged
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
29 changes: 17 additions & 12 deletions src/Core/src/Platform/Android/MauiAppCompatActivity.Lifecycle.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,18 +27,7 @@ protected override void OnActivityResult(int requestCode, Result resultCode, Int
public override void OnBackPressed()
#pragma warning restore 809
{
var preventBackPropagation = false;
IPlatformApplication.Current?.Services?.InvokeLifecycleEvents<AndroidLifecycle.OnBackPressed>(del =>
{
preventBackPropagation = del(this) || preventBackPropagation;
});

if (!preventBackPropagation)
#pragma warning disable CA1416 // Validate platform compatibility
#pragma warning disable CA1422 // Validate platform compatibility
base.OnBackPressed();
#pragma warning restore CA1422 // Validate platform compatibility
#pragma warning restore CA1416 // Validate platform compatibility
HandleBackNavigation();
}

public override void OnConfigurationChanged(Configuration newConfig)
Expand Down Expand Up @@ -145,5 +134,21 @@ public override bool OnKeyUp(Keycode keyCode, KeyEvent? e)

return handled || base.OnKeyUp(keyCode, e);
}

/// <summary>
/// Central handler used by both legacy <see cref="OnBackPressed"/> and the Android 13+ predictive back gesture callback.
/// Implements lifecycle event invocation and default back stack propagation unless explicitly prevented.
/// </summary>
void HandleBackNavigation()
{
var preventBackPropagation = false;
IPlatformApplication.Current?.Services?.InvokeLifecycleEvents<AndroidLifecycle.OnBackPressed>(del =>
{
preventBackPropagation = del(this) || preventBackPropagation;
});

if (!preventBackPropagation)
base.OnBackPressed();
}
}
}
37 changes: 37 additions & 0 deletions src/Core/src/Platform/Android/MauiAppCompatActivity.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using System;
using Android.OS;
using Android.Views;
using Android.Window;
using AndroidX.Activity;
using AndroidX.AppCompat.App;
using AndroidX.Core.Content.Resources;
Expand Down Expand Up @@ -30,10 +32,28 @@ protected override void OnCreate(Bundle? savedInstanceState)
{
this.CreatePlatformWindow(IPlatformApplication.Current.Application, savedInstanceState);
}

// Register predictive back callback (Android 13+/API 33+) if available.
// This integrates MAUI lifecycle OnBackPressed events with the system back gesture animation.
// Guidance: route custom back handling through AndroidX OnBackPressedDispatcher so
// predictive back works correctly:
// https://developer.android.com/guide/navigation/custom-back/predictive-back-gesture#update-custom
if (OperatingSystem.IsAndroidVersionAtLeast(33) && _predictiveBackCallback is null)
{
_predictiveBackCallback = new PredictiveBackCallback(this);
// Priority 0 = PRIORITY_DEFAULT: callback invoked only when no higher-priority callback handles the event
OnBackInvokedDispatcher?.RegisterOnBackInvokedCallback(0, _predictiveBackCallback);
}
}

protected override void OnDestroy()
{
if (OperatingSystem.IsAndroidVersionAtLeast(33) && _predictiveBackCallback is not null)
{
OnBackInvokedDispatcher?.UnregisterOnBackInvokedCallback(_predictiveBackCallback);
_predictiveBackCallback.Dispose();
_predictiveBackCallback = null;
}
base.OnDestroy();
}

Expand All @@ -52,5 +72,22 @@ public override bool DispatchTouchEvent(MotionEvent? e)

return handled || implHandled;
}

PredictiveBackCallback? _predictiveBackCallback;

sealed class PredictiveBackCallback : Java.Lang.Object, IOnBackInvokedCallback
{
readonly MauiAppCompatActivity _activity;
public PredictiveBackCallback(MauiAppCompatActivity activity)
{
_activity = activity;
}

public void OnBackInvoked()
{
// Reuse unified handling (will invoke lifecycle events and conditionally propagate).
_activity.HandleBackNavigation();
}
}
}
}
Loading