From 89629fb0643cc6db822f91d6058b984efc27b366 Mon Sep 17 00:00:00 2001 From: Justin McCandless Date: Fri, 18 Aug 2023 15:00:22 -0700 Subject: [PATCH 1/9] Seems to work whether Flutter is the first acitivty or not --- .../embedding/android/FlutterFragment.java | 69 +++++++++++++++++++ .../android/FlutterFragmentActivity.java | 2 + 2 files changed, 71 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..95a8d1d0c6e4c 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,55 @@ public boolean popSystemNavigator() { return false; } + @Override + public void setFrameworkHandlesBack(boolean frameworkHandlesBack) { + Log.e( + "justin", + "setFrameworkHandlesBack in FlutterFragment! setEnabled: " + frameworkHandlesBack); + 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) { + 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( From bf4e1ecb5f0c7c3847ccbc4d0a9309e0fe560d3a Mon Sep 17 00:00:00 2001 From: Justin McCandless Date: Fri, 18 Aug 2023 16:00:05 -0700 Subject: [PATCH 2/9] Work with original onBackPressed mechanics --- .../embedding/android/FlutterFragment.java | 72 +++---------------- .../android/FlutterFragmentActivity.java | 6 +- 2 files changed, 12 insertions(+), 66 deletions(-) diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterFragment.java b/shell/platform/android/io/flutter/embedding/android/FlutterFragment.java index 95a8d1d0c6e4c..bf766dac15e24 100644 --- a/shell/platform/android/io/flutter/embedding/android/FlutterFragment.java +++ b/shell/platform/android/io/flutter/embedding/android/FlutterFragment.java @@ -14,8 +14,6 @@ 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; @@ -172,8 +170,6 @@ 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 @@ -1027,20 +1023,6 @@ 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. @@ -1078,11 +1060,14 @@ 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); + // By default, Android handles backs, and predictive back is enabled. This + // can be changed by calling setFrameworkHandlesBack. For example, the + // framework will call this automatically in a typical app when it has + // routes to pop. + onBackPressedCallback.setEnabled(false); } - */ context.registerComponentCallbacks(this); } @@ -1706,51 +1691,10 @@ public boolean popSystemNavigator() { @Override public void setFrameworkHandlesBack(boolean frameworkHandlesBack) { - Log.e( - "justin", - "setFrameworkHandlesBack in FlutterFragment! setEnabled: " + frameworkHandlesBack); - 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) { - 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; + if (getArguments().getBoolean(ARG_SHOULD_AUTOMATICALLY_HANDLE_ON_BACK_PRESSED, false)) { + return; } + onBackPressedCallback.setEnabled(frameworkHandlesBack); } @VisibleForTesting diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterFragmentActivity.java b/shell/platform/android/io/flutter/embedding/android/FlutterFragmentActivity.java index 6463959491e1b..5cb028d9ed7f9 100644 --- a/shell/platform/android/io/flutter/embedding/android/FlutterFragmentActivity.java +++ b/shell/platform/android/io/flutter/embedding/android/FlutterFragmentActivity.java @@ -521,6 +521,7 @@ protected FlutterFragment createFlutterFragment() { ? TransparencyMode.opaque : TransparencyMode.transparent; final boolean shouldDelayFirstAndroidViewDraw = renderMode == RenderMode.surface; + final boolean shouldAutomaticallyHandleOnBackPressed = Build.VERSION.SDK_INT >= 33; if (getCachedEngineId() != null) { Log.v( @@ -545,8 +546,7 @@ 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) + .shouldAutomaticallyHandleOnBackPressed(shouldAutomaticallyHandleOnBackPressed) .build(); } else { Log.v( @@ -583,6 +583,7 @@ protected FlutterFragment createFlutterFragment() { .transparencyMode(transparencyMode) .shouldAttachEngineToActivity(shouldAttachEngineToActivity()) .shouldDelayFirstAndroidViewDraw(shouldDelayFirstAndroidViewDraw) + .shouldAutomaticallyHandleOnBackPressed(shouldAutomaticallyHandleOnBackPressed) .build(); } @@ -598,6 +599,7 @@ protected FlutterFragment createFlutterFragment() { .transparencyMode(transparencyMode) .shouldAttachEngineToActivity(shouldAttachEngineToActivity()) .shouldDelayFirstAndroidViewDraw(shouldDelayFirstAndroidViewDraw) + .shouldAutomaticallyHandleOnBackPressed(shouldAutomaticallyHandleOnBackPressed) .build(); } } From 1ae637d204daa9cf7530f0cec210ccd494f05f6f Mon Sep 17 00:00:00 2001 From: Justin McCandless Date: Mon, 22 Apr 2024 15:56:01 -0700 Subject: [PATCH 3/9] Fix from math1man review https://github.com/flutter/engine/pull/44865\#discussion_r1300057387 --- .../embedding/android/FlutterFragmentActivity.java | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterFragmentActivity.java b/shell/platform/android/io/flutter/embedding/android/FlutterFragmentActivity.java index 72e5746c46e7c..fda8a7c21da97 100644 --- a/shell/platform/android/io/flutter/embedding/android/FlutterFragmentActivity.java +++ b/shell/platform/android/io/flutter/embedding/android/FlutterFragmentActivity.java @@ -518,7 +518,7 @@ protected FlutterFragment createFlutterFragment() { ? TransparencyMode.opaque : TransparencyMode.transparent; final boolean shouldDelayFirstAndroidViewDraw = renderMode == RenderMode.surface; - final boolean shouldAutomaticallyHandleOnBackPressed = Build.VERSION.SDK_INT >= 33; + final boolean shouldAutomaticallyHandleOnBackPressed = true; if (getCachedEngineId() != null) { Log.v( @@ -620,12 +620,6 @@ protected void onNewIntent(@NonNull Intent intent) { super.onNewIntent(intent); } - @Override - @SuppressWarnings("MissingSuperCall") - public void onBackPressed() { - flutterFragment.onBackPressed(); - } - @Override public void onRequestPermissionsResult( int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { From 7cd33833a945f173ac7916b7e4c9b5a097a6edfe Mon Sep 17 00:00:00 2001 From: Justin McCandless Date: Tue, 23 Apr 2024 16:32:36 -0700 Subject: [PATCH 4/9] popSystemNavigator should restore onBackPressedCallback to original state, not assume enabled --- .../io/flutter/embedding/android/FlutterFragment.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterFragment.java b/shell/platform/android/io/flutter/embedding/android/FlutterFragment.java index dd363c64a6f40..8113a3871a1b3 100644 --- a/shell/platform/android/io/flutter/embedding/android/FlutterFragment.java +++ b/shell/platform/android/io/flutter/embedding/android/FlutterFragment.java @@ -1668,9 +1668,14 @@ public boolean popSystemNavigator() { // Unless we disable the callback, the dispatcher call will trigger it. This will then // trigger the fragment's onBackPressed() implementation, which will call through to the // dart side and likely call back through to this method, creating an infinite call loop. - onBackPressedCallback.setEnabled(false); + boolean enabledAtStart = onBackPressedCallback.isEnabled(); + if (enabledAtStart) { + onBackPressedCallback.setEnabled(false); + } activity.getOnBackPressedDispatcher().onBackPressed(); - onBackPressedCallback.setEnabled(true); + if (enabledAtStart) { + onBackPressedCallback.setEnabled(true); + } return true; } } From 37333321be5b1696f027a53dbfa60f6dc7a3eb46 Mon Sep 17 00:00:00 2001 From: Justin McCandless Date: Wed, 24 Apr 2024 10:35:58 -0700 Subject: [PATCH 5/9] Test that popSystemNavigator preserves enabled state --- .../flutter/embedding/android/FlutterFragmentTest.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/shell/platform/android/test/io/flutter/embedding/android/FlutterFragmentTest.java b/shell/platform/android/test/io/flutter/embedding/android/FlutterFragmentTest.java index 06b30a99e024e..872449f22c3e4 100644 --- a/shell/platform/android/test/io/flutter/embedding/android/FlutterFragmentTest.java +++ b/shell/platform/android/test/io/flutter/embedding/android/FlutterFragmentTest.java @@ -361,10 +361,20 @@ public void handleOnBackPressed() { TestDelegateFactory delegateFactory = new TestDelegateFactory(mockDelegate); fragment.setDelegateFactory(delegateFactory); + assertTrue(callback.isEnabled()); + assertTrue(fragment.popSystemNavigator()); verify(mockDelegate, never()).onBackPressed(); assertTrue(onBackPressedCalled.get()); + assertTrue(callback.isEnabled()); + + callback.setEnabled(false); + assertFalse(callback.isEnabled()); + assertTrue(fragment.popSystemNavigator()); + + verify(mockDelegate, never()).onBackPressed(); + assertFalse(callback.isEnabled()); } @Test From 0d95e27cc2799df847bd410f4a9aa6782530cd75 Mon Sep 17 00:00:00 2001 From: Justin McCandless Date: Wed, 24 Apr 2024 13:39:07 -0700 Subject: [PATCH 6/9] An attempt at testing the callback being enabled/disabled, but not working --- .../io/flutter/embedding/android/FlutterFragmentTest.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/shell/platform/android/test/io/flutter/embedding/android/FlutterFragmentTest.java b/shell/platform/android/test/io/flutter/embedding/android/FlutterFragmentTest.java index 872449f22c3e4..59719411eaa4a 100644 --- a/shell/platform/android/test/io/flutter/embedding/android/FlutterFragmentTest.java +++ b/shell/platform/android/test/io/flutter/embedding/android/FlutterFragmentTest.java @@ -290,7 +290,7 @@ private FragmentActivity getMockFragmentActivity() { } @Test - public void itDelegatesOnBackPressedAutomaticallyWhenEnabled() { + public void itDelegatesOnBackPressedWithSetFrameworkHandlesBack() { // We need to mock FlutterJNI to avoid triggering native code. FlutterJNI flutterJNI = mock(FlutterJNI.class); when(flutterJNI.isAttached()).thenReturn(true); @@ -320,6 +320,11 @@ public void itDelegatesOnBackPressedAutomaticallyWhenEnabled() { activity.onBackPressed(); + verify(mockDelegate, times(0)).onBackPressed(); + + fragment.setFrameworkHandlesBack(true); + activity.onBackPressed(); + verify(mockDelegate, times(1)).onBackPressed(); } From c06300a517849d8204a11c2d3a20f55b7e4e13e7 Mon Sep 17 00:00:00 2001 From: Justin McCandless Date: Fri, 17 May 2024 16:36:28 -0700 Subject: [PATCH 7/9] Fix flipped logic, and add comments explaining what's going on --- .../io/flutter/embedding/android/FlutterFragment.java | 2 +- .../io/flutter/embedding/android/FlutterFragmentTest.java | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterFragment.java b/shell/platform/android/io/flutter/embedding/android/FlutterFragment.java index 8113a3871a1b3..6ca690a453254 100644 --- a/shell/platform/android/io/flutter/embedding/android/FlutterFragment.java +++ b/shell/platform/android/io/flutter/embedding/android/FlutterFragment.java @@ -1685,7 +1685,7 @@ public boolean popSystemNavigator() { @Override public void setFrameworkHandlesBack(boolean frameworkHandlesBack) { - if (getArguments().getBoolean(ARG_SHOULD_AUTOMATICALLY_HANDLE_ON_BACK_PRESSED, false)) { + if (!getArguments().getBoolean(ARG_SHOULD_AUTOMATICALLY_HANDLE_ON_BACK_PRESSED, false)) { return; } onBackPressedCallback.setEnabled(frameworkHandlesBack); diff --git a/shell/platform/android/test/io/flutter/embedding/android/FlutterFragmentTest.java b/shell/platform/android/test/io/flutter/embedding/android/FlutterFragmentTest.java index 59719411eaa4a..0f428eaf4e43e 100644 --- a/shell/platform/android/test/io/flutter/embedding/android/FlutterFragmentTest.java +++ b/shell/platform/android/test/io/flutter/embedding/android/FlutterFragmentTest.java @@ -301,6 +301,8 @@ public void itDelegatesOnBackPressedWithSetFrameworkHandlesBack() { FlutterFragment fragment = FlutterFragment.withCachedEngine("my_cached_engine") + // This enables the use of onBackPressedCallback, which is what + // sends backs to the framework if setFrameworkHandlesBack is true. .shouldAutomaticallyHandleOnBackPressed(true) .build(); FragmentActivity activity = getMockFragmentActivity(); @@ -318,13 +320,15 @@ public void itDelegatesOnBackPressedWithSetFrameworkHandlesBack() { TestDelegateFactory delegateFactory = new TestDelegateFactory(mockDelegate); fragment.setDelegateFactory(delegateFactory); + // Calling onBackPressed now will still be handled by Android (the default), + // until setFrameworkHandlesBack is set to true. activity.onBackPressed(); - verify(mockDelegate, times(0)).onBackPressed(); + // Setting setFrameworkHandlesBack to true means the delegate will receive + // the back and Android won't handle it. fragment.setFrameworkHandlesBack(true); activity.onBackPressed(); - verify(mockDelegate, times(1)).onBackPressed(); } From c785e98ac42a0cff54877e838c0ad5a4746efcc8 Mon Sep 17 00:00:00 2001 From: Justin McCandless Date: Tue, 28 May 2024 11:01:46 -0700 Subject: [PATCH 8/9] Clarify comment --- .../io/flutter/embedding/android/FlutterFragment.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterFragment.java b/shell/platform/android/io/flutter/embedding/android/FlutterFragment.java index 6ca690a453254..e26d13e80f74a 100644 --- a/shell/platform/android/io/flutter/embedding/android/FlutterFragment.java +++ b/shell/platform/android/io/flutter/embedding/android/FlutterFragment.java @@ -1056,10 +1056,13 @@ public void onAttach(@NonNull Context context) { delegate.onAttach(context); if (getArguments().getBoolean(ARG_SHOULD_AUTOMATICALLY_HANDLE_ON_BACK_PRESSED, false)) { requireActivity().getOnBackPressedDispatcher().addCallback(this, onBackPressedCallback); - // By default, Android handles backs, and predictive back is enabled. This - // can be changed by calling setFrameworkHandlesBack. For example, the - // framework will call this automatically in a typical app when it has - // routes to pop. + // When Android handles a back gesture, it pops an Activity or goes back + // to the home screen. When Flutter handles a back gesture, it pops a + // route inside of the Flutter part of the app. By default, Android + // handles back gestures, so this callback is disabled. If, for example, + // the Flutter app has routes for which it wants to handle the back + // gesture, then it will enable this callback using + // setFrameworkHandlesBack. onBackPressedCallback.setEnabled(false); } context.registerComponentCallbacks(this); From 1ff7c32ae4820b722f1cdee834f205733e48ddc2 Mon Sep 17 00:00:00 2001 From: Justin McCandless Date: Tue, 28 May 2024 11:17:14 -0700 Subject: [PATCH 9/9] Whoops, should have gotten this in the merge commit --- .../test/io/flutter/embedding/android/FlutterFragmentTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell/platform/android/test/io/flutter/embedding/android/FlutterFragmentTest.java b/shell/platform/android/test/io/flutter/embedding/android/FlutterFragmentTest.java index e686f0162f9b8..24d7c6ea32a5c 100644 --- a/shell/platform/android/test/io/flutter/embedding/android/FlutterFragmentTest.java +++ b/shell/platform/android/test/io/flutter/embedding/android/FlutterFragmentTest.java @@ -328,7 +328,7 @@ public void itDelegatesOnBackPressedWithSetFrameworkHandlesBack() { // Setting setFrameworkHandlesBack to true means the delegate will receive // the back and Android won't handle it. fragment.setFrameworkHandlesBack(true); - activity.onBackPressed(); + activity.getOnBackPressedDispatcher().onBackPressed(); verify(mockDelegate, times(1)).onBackPressed(); }