From 63e9310c02780c27ef4c92d1293d5f3b4d302626 Mon Sep 17 00:00:00 2001 From: Gary Qian Date: Fri, 9 Sep 2022 12:07:15 -0700 Subject: [PATCH 1/4] Revert "Revert "Support basic back navigation in Android 13/API 33" (#36001)" This reverts commit 06cef3086ac21b64c6d6bf63df410495eba0caa9. --- .../embedding/android/FlutterActivity.java | 50 ++++++++++++++++++- .../android/FlutterActivityTest.java | 38 ++++++++++++++ .../platform/android/test_runner/build.gradle | 6 +-- 3 files changed, 90 insertions(+), 4 deletions(-) diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java b/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java index 13137ebf7dd7b..dd502a880c800 100644 --- a/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java +++ b/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java @@ -35,6 +35,8 @@ import android.view.View; import android.view.Window; import android.view.WindowManager; +import android.window.OnBackInvokedCallback; +import android.window.OnBackInvokedDispatcher; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; @@ -495,6 +497,8 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_CREATE); + registerOnBackInvokedCallback(); + configureWindowForTransparency(); setContentView(createFlutterView()); @@ -502,6 +506,48 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { configureStatusBarForFullscreenFlutterExperience(); } + /** + * Registers the callback with OnBackInvokedDispatcher to capture back navigation gestures and + * pass them to the framework. + * + *

This replaces the deprecated onBackPressed method override in order to support API 33's + * predictive back navigation feature. + * + *

The callback must be unregistered in order to prevent unpredictable behavior once outside + * the Flutter app. + */ + @VisibleForTesting + public void registerOnBackInvokedCallback() { + if (Build.VERSION.SDK_INT >= 33) { + getOnBackInvokedDispatcher() + .registerOnBackInvokedCallback( + OnBackInvokedDispatcher.PRIORITY_DEFAULT, onBackInvokedCallback); + } + } + + /** + * Unregisters the callback from OnBackInvokedDispatcher. + * + *

This should be called when the activity is no longer in use to prevent unpredictable + * behavior such as being stuck and unable to press back. + */ + @VisibleForTesting + public void unregisterOnBackInvokedCallback() { + if (Build.VERSION.SDK_INT >= 33) { + getOnBackInvokedDispatcher().unregisterOnBackInvokedCallback(onBackInvokedCallback); + } + } + + private final OnBackInvokedCallback onBackInvokedCallback = + Build.VERSION.SDK_INT >= 33 + ? new OnBackInvokedCallback() { + @Override + public void onBackInvoked() { + onBackPressed(); + } + } + : null; + /** * 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. @@ -680,7 +726,9 @@ protected void onSaveInstanceState(Bundle outState) { * *

After calling, this activity should be disposed immediately and not be re-used. */ - private void release() { + @VisibleForTesting + public void release() { + unregisterOnBackInvokedCallback(); if (delegate != null) { delegate.release(); delegate = null; 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 d6d1d30d442f0..75d7abfcb4f03 100644 --- a/shell/platform/android/test/io/flutter/embedding/android/FlutterActivityTest.java +++ b/shell/platform/android/test/io/flutter/embedding/android/FlutterActivityTest.java @@ -88,6 +88,36 @@ public void flutterViewHasId() { assertTrue(activity.findViewById(FlutterActivity.FLUTTER_VIEW_ID) instanceof FlutterView); } + // TODO(garyq): Robolectric does not yet support android api 33 yet. Switch to a robolectric + // test that directly exercises the OnBackInvoked APIs when API 33 is supported. + @Test + @TargetApi(33) + public void itRegistersOnBackInvokedCallbackOnCreate() { + Intent intent = FlutterActivityWithReportFullyDrawn.createDefaultIntent(ctx); + ActivityController activityController = + Robolectric.buildActivity(FlutterActivityWithReportFullyDrawn.class, intent); + FlutterActivityWithReportFullyDrawn activity = spy(activityController.get()); + + activity.onCreate(null); + + verify(activity, times(1)).registerOnBackInvokedCallback(); + } + + // TODO(garyq): Robolectric does not yet support android api 33 yet. Switch to a robolectric + // test that directly exercises the OnBackInvoked APIs when API 33 is supported. + @Test + @TargetApi(33) + public void itUnregistersOnBackInvokedCallbackOnRelease() { + Intent intent = FlutterActivityWithReportFullyDrawn.createDefaultIntent(ctx); + ActivityController activityController = + Robolectric.buildActivity(FlutterActivityWithReportFullyDrawn.class, intent); + FlutterActivityWithReportFullyDrawn activity = spy(activityController.get()); + + activity.release(); + + verify(activity, times(1)).unregisterOnBackInvokedCallback(); + } + @Test public void itCreatesDefaultIntentWithExpectedDefaults() { Intent intent = FlutterActivity.createDefaultIntent(ctx); @@ -596,6 +626,14 @@ public void resetFullyDrawn() { } } + private class FlutterActivityWithMockBackInvokedHandling extends FlutterActivity { + @Override + public void registerOnBackInvokedCallback() {} + + @Override + public void unregisterOnBackInvokedCallback() {} + } + private static final class FakeFlutterPlugin implements FlutterPlugin, ActivityAware, diff --git a/shell/platform/android/test_runner/build.gradle b/shell/platform/android/test_runner/build.gradle index dfdf361aeeae0..d1dfa4cc8d37b 100644 --- a/shell/platform/android/test_runner/build.gradle +++ b/shell/platform/android/test_runner/build.gradle @@ -71,10 +71,10 @@ android { testImplementation "com.google.android.play:core:1.8.0" testImplementation "com.ibm.icu:icu4j:69.1" testImplementation "org.robolectric:robolectric:4.7.3" - testImplementation "junit:junit:4.13" - testImplementation "androidx.test.ext:junit:1.1.3" + testImplementation "junit:junit:4.13.2" + testImplementation "androidx.test.ext:junit:1.1.4-alpha07" - def mockitoVersion = "4.1.0" + def mockitoVersion = "4.7.0" testImplementation "org.mockito:mockito-core:$mockitoVersion" testImplementation "org.mockito:mockito-inline:$mockitoVersion" testImplementation "org.mockito:mockito-android:$mockitoVersion" From 91946851fb1687e4ca65951cee33963b98e39686 Mon Sep 17 00:00:00 2001 From: Gary Qian Date: Fri, 9 Sep 2022 12:15:41 -0700 Subject: [PATCH 2/4] Add suppress warning to workaround google3 lint bug --- .../android/io/flutter/embedding/android/FlutterActivity.java | 1 + 1 file changed, 1 insertion(+) diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java b/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java index dd502a880c800..74188a58bf79d 100644 --- a/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java +++ b/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java @@ -541,6 +541,7 @@ public void unregisterOnBackInvokedCallback() { private final OnBackInvokedCallback onBackInvokedCallback = Build.VERSION.SDK_INT >= 33 ? new OnBackInvokedCallback() { + @SuppressWarnings("Override") @Override public void onBackInvoked() { onBackPressed(); From 4945dfc35e7cb7e1071941637f513e6a1aedc16e Mon Sep 17 00:00:00 2001 From: Gary Qian Date: Fri, 9 Sep 2022 12:45:12 -0700 Subject: [PATCH 3/4] Add TODO --- .../android/io/flutter/embedding/android/FlutterActivity.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java b/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java index 74188a58bf79d..c042308018dfb 100644 --- a/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java +++ b/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java @@ -541,6 +541,9 @@ public void unregisterOnBackInvokedCallback() { private final OnBackInvokedCallback onBackInvokedCallback = Build.VERSION.SDK_INT >= 33 ? new OnBackInvokedCallback() { + // TODO(garyq): Remove SuppressWarnings annotation. This was added to workaround + // a google3 bug where the linter is not properly running against API 33, causing + // a failure here. See b/243609613 @SuppressWarnings("Override") @Override public void onBackInvoked() { From 4e181f012c717777681862e4771af5a941774bb9 Mon Sep 17 00:00:00 2001 From: Gary Qian Date: Fri, 9 Sep 2022 12:47:34 -0700 Subject: [PATCH 4/4] Add git issue link --- .../android/io/flutter/embedding/android/FlutterActivity.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java b/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java index c042308018dfb..6f37b5d5c6a7a 100644 --- a/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java +++ b/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java @@ -543,7 +543,7 @@ public void unregisterOnBackInvokedCallback() { ? new OnBackInvokedCallback() { // TODO(garyq): Remove SuppressWarnings annotation. This was added to workaround // a google3 bug where the linter is not properly running against API 33, causing - // a failure here. See b/243609613 + // a failure here. See b/243609613 and https://github.com/flutter/flutter/issues/111295 @SuppressWarnings("Override") @Override public void onBackInvoked() {