diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java b/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java index 53344a1a3ad79..d5ffa971c5f74 100644 --- a/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java +++ b/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java @@ -217,6 +217,8 @@ public class FlutterActivity extends Activity implements FlutterActivityAndFragmentDelegate.Host, LifecycleOwner { private static final String TAG = "FlutterActivity"; + private boolean hasRegisteredBackCallback = false; + /** * The ID of the {@code FlutterView} created by this activity. * @@ -643,8 +645,6 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_CREATE); - registerOnBackInvokedCallback(); - configureWindowForTransparency(); setContentView(createFlutterView()); @@ -668,6 +668,7 @@ public void registerOnBackInvokedCallback() { getOnBackInvokedDispatcher() .registerOnBackInvokedCallback( OnBackInvokedDispatcher.PRIORITY_DEFAULT, onBackInvokedCallback); + hasRegisteredBackCallback = true; } } @@ -681,6 +682,7 @@ public void registerOnBackInvokedCallback() { public void unregisterOnBackInvokedCallback() { if (Build.VERSION.SDK_INT >= 33) { getOnBackInvokedDispatcher().unregisterOnBackInvokedCallback(onBackInvokedCallback); + hasRegisteredBackCallback = false; } } @@ -698,6 +700,15 @@ public void onBackInvoked() { } : null; + @Override + public void setFrameworkHandlesBack(boolean frameworkHandlesBacks) { + if (frameworkHandlesBacks && !hasRegisteredBackCallback) { + registerOnBackInvokedCallback(); + } else if (!frameworkHandlesBacks && hasRegisteredBackCallback) { + unregisterOnBackInvokedCallback(); + } + } + /** * Switches themes for this {@code Activity} from the theme used to launch this {@code Activity} * to a "normal theme" that is intended for regular {@code Activity} operation. diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterFragment.java b/shell/platform/android/io/flutter/embedding/android/FlutterFragment.java index 85ce3ade38fb0..ad1de8c698fd0 100644 --- a/shell/platform/android/io/flutter/embedding/android/FlutterFragment.java +++ b/shell/platform/android/io/flutter/embedding/android/FlutterFragment.java @@ -1689,6 +1689,11 @@ boolean shouldDelayFirstAndroidViewDraw() { return getArguments().getBoolean(ARG_SHOULD_DELAY_FIRST_ANDROID_VIEW_DRAW); } + @Override + public void setFrameworkHandlesBack(boolean frameworkHandlesBacks) { + // Irrelevant to FlutterFragment. + } + private boolean stillAttachedForEvent(String event) { if (delegate == null) { Log.w(TAG, "FlutterFragment " + hashCode() + " " + event + " called after release."); diff --git a/shell/platform/android/io/flutter/embedding/engine/systemchannels/PlatformChannel.java b/shell/platform/android/io/flutter/embedding/engine/systemchannels/PlatformChannel.java index da08e499f1bb6..2c0a3bf378a39 100644 --- a/shell/platform/android/io/flutter/embedding/engine/systemchannels/PlatformChannel.java +++ b/shell/platform/android/io/flutter/embedding/engine/systemchannels/PlatformChannel.java @@ -136,6 +136,13 @@ public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result.error("error", exception.getMessage(), null); } break; + case "SystemNavigator.setFrameworkHandlesBack": + { + boolean frameworkHandlesBacks = (boolean) arguments; + platformMessageHandler.setFrameworkHandlesBack(frameworkHandlesBacks); + result.success(null); + break; + } case "SystemNavigator.pop": platformMessageHandler.popSystemNavigator(); result.success(null); @@ -509,6 +516,9 @@ public interface PlatformMessageHandler { */ void setSystemUiOverlayStyle(@NonNull SystemChromeStyle systemUiOverlayStyle); + /** The Flutter application would or would not like to handle navigation pop events itself. */ + void setFrameworkHandlesBack(boolean frameworkHandlesBack); + /** * The Flutter application would like to pop the top item off of the Android app's navigation * back stack. diff --git a/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java b/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java index 55af0485bf1e6..129e3318461fc 100644 --- a/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java +++ b/shell/platform/android/io/flutter/plugin/platform/PlatformPlugin.java @@ -56,6 +56,8 @@ public interface PlatformPluginDelegate { * androidx.activity.OnBackPressedDispatcher} will be executed. */ boolean popSystemNavigator(); + + void setFrameworkHandlesBack(boolean frameworkHandlesBacks); } @VisibleForTesting @@ -109,6 +111,11 @@ public void setSystemUiOverlayStyle( setSystemChromeSystemUIOverlayStyle(systemUiOverlayStyle); } + @Override + public void setFrameworkHandlesBack(boolean frameworkHandlesBacks) { + PlatformPlugin.this.setFrameworkHandlesBack(frameworkHandlesBacks); + } + @Override public void popSystemNavigator() { PlatformPlugin.this.popSystemNavigator(); @@ -475,6 +482,10 @@ private void setSystemChromeSystemUIOverlayStyle( currentTheme = systemChromeStyle; } + private void setFrameworkHandlesBack(boolean frameworkHandlesBacks) { + platformPluginDelegate.setFrameworkHandlesBack(frameworkHandlesBacks); + } + private void popSystemNavigator() { if (platformPluginDelegate != null && platformPluginDelegate.popSystemNavigator()) { // A custom behavior was executed by the delegate. Don't execute default behavior. diff --git a/shell/platform/android/test/io/flutter/embedding/android/FlutterActivityTest.java b/shell/platform/android/test/io/flutter/embedding/android/FlutterActivityTest.java index cd5ab22165a85..d2992e656559b 100644 --- a/shell/platform/android/test/io/flutter/embedding/android/FlutterActivityTest.java +++ b/shell/platform/android/test/io/flutter/embedding/android/FlutterActivityTest.java @@ -92,7 +92,7 @@ public void flutterViewHasId() { // test that directly exercises the OnBackInvoked APIs when API 33 is supported. @Test @TargetApi(33) - public void itRegistersOnBackInvokedCallbackOnCreate() { + public void itRegistersOnBackInvokedCallbackOnChangingFrameworkHandlesBack() { Intent intent = FlutterActivityWithReportFullyDrawn.createDefaultIntent(ctx); ActivityController activityController = Robolectric.buildActivity(FlutterActivityWithReportFullyDrawn.class, intent); @@ -100,6 +100,12 @@ public void itRegistersOnBackInvokedCallbackOnCreate() { activity.onCreate(null); + verify(activity, times(0)).registerOnBackInvokedCallback(); + + activity.setFrameworkHandlesBack(false); + verify(activity, times(0)).registerOnBackInvokedCallback(); + + activity.setFrameworkHandlesBack(true); verify(activity, times(1)).registerOnBackInvokedCallback(); } diff --git a/shell/platform/android/test/io/flutter/embedding/android/FlutterAndroidComponentTest.java b/shell/platform/android/test/io/flutter/embedding/android/FlutterAndroidComponentTest.java index 602e758013f64..b7f9d9060dcfd 100644 --- a/shell/platform/android/test/io/flutter/embedding/android/FlutterAndroidComponentTest.java +++ b/shell/platform/android/test/io/flutter/embedding/android/FlutterAndroidComponentTest.java @@ -417,5 +417,8 @@ public void updateSystemUiOverlays() {} public boolean popSystemNavigator() { return false; } + + @Override + public void setFrameworkHandlesBack(boolean frameworkHandlesBacks) {} } } diff --git a/shell/platform/android/test/io/flutter/plugin/platform/PlatformPluginTest.java b/shell/platform/android/test/io/flutter/plugin/platform/PlatformPluginTest.java index 2aa09af6c83c1..8b27e80fd4264 100644 --- a/shell/platform/android/test/io/flutter/plugin/platform/PlatformPluginTest.java +++ b/shell/platform/android/test/io/flutter/plugin/platform/PlatformPluginTest.java @@ -469,6 +469,19 @@ public void verifyWindowFlagsSetToStyleOverlays() { | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION); } + @Test + public void setFrameworkHandlesBackFlutterActivity() { + Activity mockActivity = mock(Activity.class); + PlatformChannel mockPlatformChannel = mock(PlatformChannel.class); + PlatformPluginDelegate mockPlatformPluginDelegate = mock(PlatformPluginDelegate.class); + PlatformPlugin platformPlugin = + new PlatformPlugin(mockActivity, mockPlatformChannel, mockPlatformPluginDelegate); + + platformPlugin.mPlatformMessageHandler.setFrameworkHandlesBack(true); + + verify(mockPlatformPluginDelegate, times(1)).setFrameworkHandlesBack(true); + } + @Test public void popSystemNavigatorFlutterActivity() { Activity mockActivity = mock(Activity.class);