Reland "Support basic back navigation in Android 13/API 33 #35678" (flutter/engine#36051)

This commit is contained in:
Gary Qian 2022-09-12 08:52:05 -07:00 committed by GitHub
parent c024760a13
commit 218fb443b9
3 changed files with 94 additions and 4 deletions

View File

@ -35,6 +35,8 @@ import android.os.Bundle;
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 @@ public class FlutterActivity extends Activity
lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_CREATE);
registerOnBackInvokedCallback();
configureWindowForTransparency();
setContentView(createFlutterView());
@ -502,6 +506,52 @@ public class FlutterActivity extends Activity
configureStatusBarForFullscreenFlutterExperience();
}
/**
* Registers the callback with OnBackInvokedDispatcher to capture back navigation gestures and
* pass them to the framework.
*
* <p>This replaces the deprecated onBackPressed method override in order to support API 33's
* predictive back navigation feature.
*
* <p>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.
*
* <p>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() {
// 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 and https://github.com/flutter/flutter/issues/111295
@SuppressWarnings("Override")
@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 +730,9 @@ public class FlutterActivity extends Activity
*
* <p>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;

View File

@ -88,6 +88,36 @@ public class FlutterActivityTest {
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<FlutterActivityWithReportFullyDrawn> 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<FlutterActivityWithReportFullyDrawn> 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 class FlutterActivityTest {
}
}
private class FlutterActivityWithMockBackInvokedHandling extends FlutterActivity {
@Override
public void registerOnBackInvokedCallback() {}
@Override
public void unregisterOnBackInvokedCallback() {}
}
private static final class FakeFlutterPlugin
implements FlutterPlugin,
ActivityAware,

View File

@ -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"