Skip to content

Commit 2322192

Browse files
kubaflomattleibow
authored andcommitted
predictive back gesture support for Android 13+ (#32461)
* Add predictive back gesture support for Android 13+ Introduces a unified HandleBackNavigation method to centralize back navigation handling and integrates Android 13+ predictive back gesture callbacks with MAUI lifecycle events. Predictive back is registered and unregistered appropriately, ensuring custom back handling works with system back gesture animation. * Fix predictive back callback registration and resource management - Fix potential double registration by checking if callback is null before creating/registering - Add Dispose() call in OnDestroy() to prevent memory leaks - Change field type to concrete PredictiveBackCallback for better performance (CA1859) - Add comment explaining PRIORITY_DEFAULT value These changes address code review feedback while maintaining the PR's core functionality. * Simplify code to reduce nesting --------- Co-authored-by: Matthew Leibowitz <mattleibow@live.com>
1 parent 771cd21 commit 2322192

2 files changed

Lines changed: 54 additions & 12 deletions

File tree

src/Core/src/Platform/Android/MauiAppCompatActivity.Lifecycle.cs

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -27,18 +27,7 @@ protected override void OnActivityResult(int requestCode, Result resultCode, Int
2727
public override void OnBackPressed()
2828
#pragma warning restore 809
2929
{
30-
var preventBackPropagation = false;
31-
IPlatformApplication.Current?.Services?.InvokeLifecycleEvents<AndroidLifecycle.OnBackPressed>(del =>
32-
{
33-
preventBackPropagation = del(this) || preventBackPropagation;
34-
});
35-
36-
if (!preventBackPropagation)
37-
#pragma warning disable CA1416 // Validate platform compatibility
38-
#pragma warning disable CA1422 // Validate platform compatibility
39-
base.OnBackPressed();
40-
#pragma warning restore CA1422 // Validate platform compatibility
41-
#pragma warning restore CA1416 // Validate platform compatibility
30+
HandleBackNavigation();
4231
}
4332

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

146135
return handled || base.OnKeyUp(keyCode, e);
147136
}
137+
138+
/// <summary>
139+
/// Central handler used by both legacy <see cref="OnBackPressed"/> and the Android 13+ predictive back gesture callback.
140+
/// Implements lifecycle event invocation and default back stack propagation unless explicitly prevented.
141+
/// </summary>
142+
void HandleBackNavigation()
143+
{
144+
var preventBackPropagation = false;
145+
IPlatformApplication.Current?.Services?.InvokeLifecycleEvents<AndroidLifecycle.OnBackPressed>(del =>
146+
{
147+
preventBackPropagation = del(this) || preventBackPropagation;
148+
});
149+
150+
if (!preventBackPropagation)
151+
base.OnBackPressed();
152+
}
148153
}
149154
}

src/Core/src/Platform/Android/MauiAppCompatActivity.cs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
using System;
12
using Android.OS;
23
using Android.Views;
4+
using Android.Window;
35
using AndroidX.Activity;
46
using AndroidX.AppCompat.App;
57
using AndroidX.Core.Content.Resources;
@@ -30,10 +32,28 @@ protected override void OnCreate(Bundle? savedInstanceState)
3032
{
3133
this.CreatePlatformWindow(IPlatformApplication.Current.Application, savedInstanceState);
3234
}
35+
36+
// Register predictive back callback (Android 13+/API 33+) if available.
37+
// This integrates MAUI lifecycle OnBackPressed events with the system back gesture animation.
38+
// Guidance: route custom back handling through AndroidX OnBackPressedDispatcher so
39+
// predictive back works correctly:
40+
// https://developer.android.com/guide/navigation/custom-back/predictive-back-gesture#update-custom
41+
if (OperatingSystem.IsAndroidVersionAtLeast(33) && _predictiveBackCallback is null)
42+
{
43+
_predictiveBackCallback = new PredictiveBackCallback(this);
44+
// Priority 0 = PRIORITY_DEFAULT: callback invoked only when no higher-priority callback handles the event
45+
OnBackInvokedDispatcher?.RegisterOnBackInvokedCallback(0, _predictiveBackCallback);
46+
}
3347
}
3448

3549
protected override void OnDestroy()
3650
{
51+
if (OperatingSystem.IsAndroidVersionAtLeast(33) && _predictiveBackCallback is not null)
52+
{
53+
OnBackInvokedDispatcher?.UnregisterOnBackInvokedCallback(_predictiveBackCallback);
54+
_predictiveBackCallback.Dispose();
55+
_predictiveBackCallback = null;
56+
}
3757
base.OnDestroy();
3858
}
3959

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

5373
return handled || implHandled;
5474
}
75+
76+
PredictiveBackCallback? _predictiveBackCallback;
77+
78+
sealed class PredictiveBackCallback : Java.Lang.Object, IOnBackInvokedCallback
79+
{
80+
readonly MauiAppCompatActivity _activity;
81+
public PredictiveBackCallback(MauiAppCompatActivity activity)
82+
{
83+
_activity = activity;
84+
}
85+
86+
public void OnBackInvoked()
87+
{
88+
// Reuse unified handling (will invoke lifecycle events and conditionally propagate).
89+
_activity.HandleBackNavigation();
90+
}
91+
}
5592
}
5693
}

0 commit comments

Comments
 (0)