Skip to content

Commit

Permalink
Reland "Support basic back navigation in Android 13/API 33 flutter#35678
Browse files Browse the repository at this point in the history
  • Loading branch information
GaryQian authored Sep 12, 2022
1 parent e729f22 commit 41d0cc0
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -495,13 +497,61 @@ protected void onCreate(@Nullable Bundle savedInstanceState) {

lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_CREATE);

registerOnBackInvokedCallback();

configureWindowForTransparency();

setContentView(createFlutterView());

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.
Expand Down Expand Up @@ -680,7 +730,9 @@ protected void onSaveInstanceState(Bundle outState) {
*
* <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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<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);
Expand Down Expand Up @@ -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,
Expand Down
6 changes: 3 additions & 3 deletions shell/platform/android/test_runner/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down

0 comments on commit 41d0cc0

Please sign in to comment.