|
19 | 19 | import static org.mockito.Mockito.verify; |
20 | 20 | import static org.mockito.Mockito.when; |
21 | 21 |
|
| 22 | +import android.annotation.TargetApi; |
22 | 23 | import android.content.Context; |
| 24 | +import androidx.activity.BackEventCompat; |
23 | 25 | import androidx.activity.OnBackPressedCallback; |
| 26 | +import androidx.activity.OnBackPressedDispatcher; |
24 | 27 | import androidx.fragment.app.FragmentActivity; |
25 | 28 | import androidx.test.core.app.ActivityScenario; |
26 | 29 | import androidx.test.core.app.ApplicationProvider; |
27 | 30 | import androidx.test.ext.junit.runners.AndroidJUnit4; |
| 31 | +import io.flutter.Build; |
28 | 32 | import io.flutter.embedding.engine.FlutterEngine; |
29 | 33 | import io.flutter.embedding.engine.FlutterEngineCache; |
30 | 34 | import io.flutter.embedding.engine.FlutterJNI; |
|
34 | 38 | import java.util.concurrent.atomic.AtomicBoolean; |
35 | 39 | import org.junit.Test; |
36 | 40 | import org.junit.runner.RunWith; |
| 41 | +import org.robolectric.annotation.Config; |
37 | 42 |
|
38 | 43 | @RunWith(AndroidJUnit4.class) |
39 | 44 | public class FlutterFragmentTest { |
@@ -286,7 +291,9 @@ public void itReturnsExclusiveAppComponent() { |
286 | 291 | } |
287 | 292 |
|
288 | 293 | @Test |
289 | | - public void itDelegatesOnBackPressedWithSetFrameworkHandlesBack() { |
| 294 | + @Config(sdk = Build.API_LEVELS.API_33) |
| 295 | + @TargetApi(Build.API_LEVELS.API_33) |
| 296 | + public void itDelegatesOnBackPressedWithSetFrameworkHandlesBackForSdk33() { |
290 | 297 | // We need to mock FlutterJNI to avoid triggering native code. |
291 | 298 | FlutterJNI flutterJNI = mock(FlutterJNI.class); |
292 | 299 | when(flutterJNI.isAttached()).thenReturn(true); |
@@ -334,6 +341,72 @@ public void itDelegatesOnBackPressedWithSetFrameworkHandlesBack() { |
334 | 341 | } |
335 | 342 | } |
336 | 343 |
|
| 344 | + @Test |
| 345 | + @Config(sdk = Build.API_LEVELS.API_34) |
| 346 | + @TargetApi(Build.API_LEVELS.API_34) |
| 347 | + public void itDelegatesOnBackPressedWithSetFrameworkHandlesBackForSdk34OrHigher() { |
| 348 | + // We need to mock FlutterJNI to avoid triggering native code. |
| 349 | + FlutterJNI flutterJNI = mock(FlutterJNI.class); |
| 350 | + when(flutterJNI.isAttached()).thenReturn(true); |
| 351 | + |
| 352 | + FlutterEngine flutterEngine = |
| 353 | + new FlutterEngine(ctx, new FlutterLoader(), flutterJNI, null, false); |
| 354 | + FlutterEngineCache.getInstance().put("my_cached_engine", flutterEngine); |
| 355 | + |
| 356 | + FlutterFragment fragment = |
| 357 | + FlutterFragment.withCachedEngine("my_cached_engine") |
| 358 | + // This enables the use of onBackPressedCallback, which is what |
| 359 | + // sends backs to the framework if setFrameworkHandlesBack is true. |
| 360 | + .shouldAutomaticallyHandleOnBackPressed(true) |
| 361 | + .build(); |
| 362 | + |
| 363 | + try (ActivityScenario<FragmentActivity> scenario = |
| 364 | + ActivityScenario.launch(FragmentActivity.class)) { |
| 365 | + scenario.onActivity( |
| 366 | + activity -> { |
| 367 | + activity |
| 368 | + .getSupportFragmentManager() |
| 369 | + .beginTransaction() |
| 370 | + .add(android.R.id.content, fragment) |
| 371 | + .commitNow(); |
| 372 | + |
| 373 | + FlutterActivityAndFragmentDelegate mockDelegate = |
| 374 | + mock(FlutterActivityAndFragmentDelegate.class); |
| 375 | + isDelegateAttached = true; |
| 376 | + when(mockDelegate.isAttached()).thenAnswer(invocation -> isDelegateAttached); |
| 377 | + doAnswer(invocation -> isDelegateAttached = false).when(mockDelegate).onDetach(); |
| 378 | + TestDelegateFactory delegateFactory = new TestDelegateFactory(mockDelegate); |
| 379 | + fragment.setDelegateFactory(delegateFactory); |
| 380 | + |
| 381 | + BackEventCompat mockBackEvent = mock(BackEventCompat.class); |
| 382 | + OnBackPressedDispatcher dispatcher = activity.getOnBackPressedDispatcher(); |
| 383 | + |
| 384 | + // Back gesture events now will still be handled by Android (the default), |
| 385 | + // until setFrameworkHandlesBack is set to true. |
| 386 | + dispatcher.dispatchOnBackStarted(mockBackEvent); |
| 387 | + dispatcher.dispatchOnBackProgressed(mockBackEvent); |
| 388 | + dispatcher.onBackPressed(); |
| 389 | + dispatcher.dispatchOnBackCancelled(); |
| 390 | + verify(mockDelegate, times(0)).startBackGesture(any()); |
| 391 | + verify(mockDelegate, times(0)).updateBackGestureProgress(any()); |
| 392 | + verify(mockDelegate, times(0)).commitBackGesture(); |
| 393 | + verify(mockDelegate, times(0)).cancelBackGesture(); |
| 394 | + |
| 395 | + // Setting setFrameworkHandlesBack to true means the delegate will receive |
| 396 | + // the back and Android won't handle it. |
| 397 | + fragment.setFrameworkHandlesBack(true); |
| 398 | + dispatcher.dispatchOnBackStarted(mockBackEvent); |
| 399 | + dispatcher.dispatchOnBackProgressed(mockBackEvent); |
| 400 | + dispatcher.onBackPressed(); |
| 401 | + dispatcher.dispatchOnBackCancelled(); |
| 402 | + verify(mockDelegate, times(1)).startBackGesture(any()); |
| 403 | + verify(mockDelegate, times(1)).updateBackGestureProgress(any()); |
| 404 | + verify(mockDelegate, times(1)).commitBackGesture(); |
| 405 | + verify(mockDelegate, times(1)).cancelBackGesture(); |
| 406 | + }); |
| 407 | + } |
| 408 | + } |
| 409 | + |
337 | 410 | @Test |
338 | 411 | public void itHandlesPopSystemNavigationAutomaticallyWhenEnabled() { |
339 | 412 | // We need to mock FlutterJNI to avoid triggering native code. |
|
0 commit comments