diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterActivityAndFragmentDelegate.java b/shell/platform/android/io/flutter/embedding/android/FlutterActivityAndFragmentDelegate.java index d22118d4391b1..90f152e1d71dc 100644 --- a/shell/platform/android/io/flutter/embedding/android/FlutterActivityAndFragmentDelegate.java +++ b/shell/platform/android/io/flutter/embedding/android/FlutterActivityAndFragmentDelegate.java @@ -85,6 +85,7 @@ public interface DelegateFactory { @VisibleForTesting @Nullable FlutterView flutterView; @Nullable private PlatformPlugin platformPlugin; @VisibleForTesting @Nullable OnPreDrawListener activePreDrawListener; + @VisibleForTesting boolean hasPushedRouteInformation = false; private boolean isFlutterEngineFromHost; private boolean isFlutterUiDisplayed; private boolean isFirstFrameRendered; @@ -92,14 +93,25 @@ public interface DelegateFactory { private Integer previousVisibility; @Nullable private FlutterEngineGroup engineGroup; - @NonNull - private final FlutterUiDisplayListener flutterUiDisplayListener = + @NonNull @VisibleForTesting + final FlutterUiDisplayListener flutterUiDisplayListener = new FlutterUiDisplayListener() { @Override public void onFlutterUiDisplayed() { host.onFlutterUiDisplayed(); isFlutterUiDisplayed = true; isFirstFrameRendered = true; + + // Push updated route information if not pushed by onNewIntent(Intent) + // after the first frame has been drawn to ensure it is not ignored. + if (flutterEngine != null && !hasPushedRouteInformation) { + ensureAlive(); + String initialRoute = maybeGetInitialRouteFromIntent(host.getActivity().getIntent()); + if (initialRoute != null && !initialRoute.isEmpty()) { + flutterEngine.getNavigationChannel().pushRouteInformation(initialRoute); + hasPushedRouteInformation = true; + } + } } @Override @@ -836,6 +848,7 @@ void onNewIntent(@NonNull Intent intent) { String initialRoute = maybeGetInitialRouteFromIntent(intent); if (initialRoute != null && !initialRoute.isEmpty()) { flutterEngine.getNavigationChannel().pushRouteInformation(initialRoute); + hasPushedRouteInformation = true; } } else { Log.w(TAG, "onNewIntent() invoked before FlutterFragment was attached to an Activity."); diff --git a/shell/platform/android/test/io/flutter/embedding/android/FlutterActivityAndFragmentDelegateTest.java b/shell/platform/android/test/io/flutter/embedding/android/FlutterActivityAndFragmentDelegateTest.java index e120d7791a6d2..d9abf463d9ae7 100644 --- a/shell/platform/android/test/io/flutter/embedding/android/FlutterActivityAndFragmentDelegateTest.java +++ b/shell/platform/android/test/io/flutter/embedding/android/FlutterActivityAndFragmentDelegateTest.java @@ -5,12 +5,14 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isNotNull; import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -1189,6 +1191,36 @@ public void usesFlutterEngineGroup() { assertEquals(engineUnderTest, mockFlutterEngine); } + @Test + public void pushesRouteInformationAfterFirstFrameIfApplicable() { + FlutterActivityAndFragmentDelegate delegate = new FlutterActivityAndFragmentDelegate(mockHost); + Activity mockActivity = mock(Activity.class); + Intent mockIntent = mock(Intent.class); + + delegate.onAttach(ctx); + FlutterUiDisplayListener flutterUiDisplayListener = delegate.flutterUiDisplayListener; + when(mockHost.shouldHandleDeeplinking()).thenReturn(true); + when(mockHost.getActivity()).thenReturn(mockActivity); + when(mockActivity.getIntent()).thenReturn(mockIntent); + when(mockIntent.getData()).thenReturn(Uri.parse("http://myApp/custom/route?query=test")); + + // Test case where updated route information has not already been pushed. + flutterUiDisplayListener.onFlutterUiDisplayed(); + + verify(mockFlutterEngine.getNavigationChannel(), times(1)) + .pushRouteInformation("/custom/route?query=test"); + assertTrue(delegate.hasPushedRouteInformation); + + // Test case where updated route information has already been pushed. + reset(mockFlutterEngine.getNavigationChannel()); + delegate.hasPushedRouteInformation = true; + + flutterUiDisplayListener.onFlutterUiDisplayed(); + + verify(mockFlutterEngine.getNavigationChannel(), times(0)) + .pushRouteInformation(any(String.class)); + } + /** * Creates a mock {@link io.flutter.embedding.engine.FlutterEngine}. *