From 0718e5c24f290304d972cbd8fd1cb66e1d226eec Mon Sep 17 00:00:00 2001 From: Justin McCandless Date: Fri, 18 Aug 2023 15:00:22 -0700 Subject: [PATCH] WIP partially working --- .../embedding/android/FlutterFragment.java | 74 +++++++++++++++++++ .../android/FlutterFragmentActivity.java | 2 + 2 files changed, 76 insertions(+) diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterFragment.java b/shell/platform/android/io/flutter/embedding/android/FlutterFragment.java index 586d60c9c6980..18834c44fe9ff 100644 --- a/shell/platform/android/io/flutter/embedding/android/FlutterFragment.java +++ b/shell/platform/android/io/flutter/embedding/android/FlutterFragment.java @@ -14,6 +14,8 @@ import android.view.View; import android.view.ViewGroup; import android.view.ViewTreeObserver.OnWindowFocusChangeListener; +import android.window.OnBackInvokedCallback; +import android.window.OnBackInvokedDispatcher; import androidx.activity.OnBackPressedCallback; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -170,6 +172,8 @@ public class FlutterFragment extends Fragment protected static final String ARG_SHOULD_AUTOMATICALLY_HANDLE_ON_BACK_PRESSED = "should_automatically_handle_on_back_pressed"; + private boolean hasRegisteredBackCallback = false; + @RequiresApi(18) private final OnWindowFocusChangeListener onWindowFocusChangeListener = Build.VERSION.SDK_INT >= 18 @@ -1023,6 +1027,20 @@ public void handleOnBackPressed() { } }; + 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; + public FlutterFragment() { // Ensure that we at least have an empty Bundle of arguments so that we don't // need to continually check for null arguments before grabbing one. @@ -1060,9 +1078,11 @@ public void onAttach(@NonNull Context context) { super.onAttach(context); delegate = delegateFactory.createDelegate(this); delegate.onAttach(context); + /* if (getArguments().getBoolean(ARG_SHOULD_AUTOMATICALLY_HANDLE_ON_BACK_PRESSED, false)) { requireActivity().getOnBackPressedDispatcher().addCallback(this, onBackPressedCallback); } + */ context.registerComponentCallbacks(this); } @@ -1684,6 +1704,60 @@ public boolean popSystemNavigator() { return false; } + @Override + public void setFrameworkHandlesBack(boolean frameworkHandlesBack) { + Log.e( + "justin", + "setFrameworkHandlesBack in FlutterFragment! setEnabled: " + frameworkHandlesBack); + // TODO(justinmc): Doesn't quite work. + // Home route: pback works (good) + // Second route: pback doesn't work (good) + // Home route again: pback still doesn't work (bad) though I received the right call here... + if (frameworkHandlesBack && !hasRegisteredBackCallback) { + registerOnBackInvokedCallback(); + } else if (!frameworkHandlesBack && hasRegisteredBackCallback) { + unregisterOnBackInvokedCallback(); + } + } + + /** + * Registers the callback with OnBackPressedDispatcher 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) { + requireActivity().getOnBackPressedDispatcher().addCallback(this, onBackPressedCallback); + getActivity() + .getOnBackInvokedDispatcher() + .registerOnBackInvokedCallback( + OnBackInvokedDispatcher.PRIORITY_DEFAULT, onBackInvokedCallback); + hasRegisteredBackCallback = true; + } + } + + /** + * Unregisters the callback from OnBackPressedDispatcher. + * + *

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) { + getActivity() + .getOnBackInvokedDispatcher() + .unregisterOnBackInvokedCallback(onBackInvokedCallback); + hasRegisteredBackCallback = false; + } + } + @VisibleForTesting @NonNull boolean shouldDelayFirstAndroidViewDraw() { diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterFragmentActivity.java b/shell/platform/android/io/flutter/embedding/android/FlutterFragmentActivity.java index 4f3ab481b9ee2..6463959491e1b 100644 --- a/shell/platform/android/io/flutter/embedding/android/FlutterFragmentActivity.java +++ b/shell/platform/android/io/flutter/embedding/android/FlutterFragmentActivity.java @@ -545,6 +545,8 @@ protected FlutterFragment createFlutterFragment() { .shouldAttachEngineToActivity(shouldAttachEngineToActivity()) .destroyEngineWithFragment(shouldDestroyEngineWithHost()) .shouldDelayFirstAndroidViewDraw(shouldDelayFirstAndroidViewDraw) + // TODO(justinmc): Unsure whether this is relevant to predictive back. + // .shouldAutomaticallyHandleOnBackPressed(Build.VERSION.SDK_INT >= 33) .build(); } else { Log.v(