Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit bc1059c

Browse files
committed
addressing comments
1 parent fe25a89 commit bc1059c

File tree

8 files changed

+201
-54
lines changed

8 files changed

+201
-54
lines changed

shell/platform/android/io/flutter/embedding/android/FlutterActivity.java

Lines changed: 35 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -447,10 +447,9 @@ protected void onCreate(@Nullable Bundle savedInstanceState) {
447447
*/
448448
private void switchLaunchThemeForNormalTheme() {
449449
try {
450-
ActivityInfo activityInfo =
451-
getPackageManager().getActivityInfo(getComponentName(), PackageManager.GET_META_DATA);
452-
if (activityInfo.metaData != null) {
453-
int normalThemeRID = activityInfo.metaData.getInt(NORMAL_THEME_META_DATA_KEY, -1);
450+
Bundle metaData = getMetaData();
451+
if (metaData != null) {
452+
int normalThemeRID = metaData.getInt(NORMAL_THEME_META_DATA_KEY, -1);
454453
if (normalThemeRID != -1) {
455454
setTheme(normalThemeRID);
456455
}
@@ -486,9 +485,7 @@ public SplashScreen provideSplashScreen() {
486485
@SuppressWarnings("deprecation")
487486
private Drawable getSplashScreenFromManifest() {
488487
try {
489-
ActivityInfo activityInfo =
490-
getPackageManager().getActivityInfo(getComponentName(), PackageManager.GET_META_DATA);
491-
Bundle metadata = activityInfo.metaData;
488+
Bundle metadata = getMetaData();
492489
int splashScreenId = metadata != null ? metadata.getInt(SPLASH_SCREEN_META_DATA_KEY) : 0;
493490
return splashScreenId != 0
494491
? Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP
@@ -749,9 +746,7 @@ public boolean shouldDestroyEngineWithHost() {
749746
@NonNull
750747
public String getDartEntrypointFunctionName() {
751748
try {
752-
ActivityInfo activityInfo =
753-
getPackageManager().getActivityInfo(getComponentName(), PackageManager.GET_META_DATA);
754-
Bundle metadata = activityInfo.metaData;
749+
Bundle metadata = getMetaData();
755750
String desiredDartEntrypoint =
756751
metadata != null ? metadata.getString(DART_ENTRYPOINT_META_DATA_KEY) : null;
757752
return desiredDartEntrypoint != null ? desiredDartEntrypoint : DEFAULT_DART_ENTRYPOINT;
@@ -781,19 +776,16 @@ public String getDartEntrypointFunctionName() {
781776
*
782777
* <p>Subclasses may override this method to directly control the initial route.
783778
*
784-
* <p>If this method returns null and the {@code shouldHandleDeeplinking()} returns true, the
785-
* {@link FlutterActivityAndFragmentDelegate} retrieves the initial route from the {@code Intent}
786-
* through the Intent.getData() instead.
779+
* <p>If this method returns null and the {@code shouldHandleDeeplinking} returns true, the
780+
* initial route is derived from the {@code Intent} through the Intent.getData() instead.
787781
*/
788782
public String getInitialRoute() {
789783
if (getIntent().hasExtra(EXTRA_INITIAL_ROUTE)) {
790784
return getIntent().getStringExtra(EXTRA_INITIAL_ROUTE);
791785
}
792786

793787
try {
794-
ActivityInfo activityInfo =
795-
getPackageManager().getActivityInfo(getComponentName(), PackageManager.GET_META_DATA);
796-
Bundle metadata = activityInfo.metaData;
788+
Bundle metadata = getMetaData();
797789
String desiredInitialRoute =
798790
metadata != null ? metadata.getString(INITIAL_ROUTE_META_DATA_KEY) : null;
799791
return desiredInitialRoute;
@@ -898,6 +890,25 @@ protected FlutterEngine getFlutterEngine() {
898890
return delegate.getFlutterEngine();
899891
}
900892

893+
private Bundle cachedMetaData;
894+
895+
/** Mocks the meta data for testing purposes. */
896+
@VisibleForTesting
897+
public void setMetaData(Bundle metaData) {
898+
cachedMetaData = metaData;
899+
};
900+
901+
/** Retrieves the meta data specified in the AndroidManifest.xml. */
902+
@Nullable
903+
protected Bundle getMetaData() throws PackageManager.NameNotFoundException {
904+
if (cachedMetaData == null) {
905+
ActivityInfo activityInfo =
906+
getPackageManager().getActivityInfo(getComponentName(), PackageManager.GET_META_DATA);
907+
cachedMetaData = activityInfo.metaData;
908+
}
909+
return cachedMetaData;
910+
}
911+
901912
@Nullable
902913
@Override
903914
public PlatformPlugin providePlatformPlugin(
@@ -974,12 +985,17 @@ public boolean shouldAttachEngineToActivity() {
974985
return true;
975986
}
976987

988+
/**
989+
* Whether to handle the deeplinking from the {@code Intent} automatically if the {@code
990+
* getInitialRoute} returns null.
991+
*
992+
* <p>The default implementation looks for the value of the key `flutter_handle_deeplinking` in
993+
* the AndroidManifest.xml.
994+
*/
977995
@Override
978996
public boolean shouldHandleDeeplinking() {
979997
try {
980-
ActivityInfo activityInfo =
981-
getPackageManager().getActivityInfo(getComponentName(), PackageManager.GET_META_DATA);
982-
Bundle metadata = activityInfo.metaData;
998+
Bundle metadata = getMetaData();
983999
boolean shouldHandleDeeplinking =
9841000
metadata != null ? metadata.getBoolean(HANDLE_DEEPLINKING_META_DATA_KEY) : false;
9851001
return shouldHandleDeeplinking;

shell/platform/android/io/flutter/embedding/android/FlutterActivityAndFragmentDelegate.java

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
package io.flutter.embedding.android;
66

77
import static android.content.ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW;
8+
import static io.flutter.embedding.android.FlutterActivityLaunchConfigs.DEFAULT_INITIAL_ROUTE;
89

910
import android.app.Activity;
1011
import android.content.Context;
@@ -366,6 +367,9 @@ private void doInitialFlutterViewRun() {
366367
String initialRoute = host.getInitialRoute();
367368
if (initialRoute == null) {
368369
initialRoute = maybeGetInitialRouteFromIntent(host.getActivity().getIntent());
370+
if (initialRoute == null) {
371+
initialRoute = DEFAULT_INITIAL_ROUTE;
372+
}
369373
}
370374
Log.v(
371375
TAG,
@@ -376,9 +380,7 @@ private void doInitialFlutterViewRun() {
376380

377381
// The engine needs to receive the Flutter app's initial route before executing any
378382
// Dart code to ensure that the initial route arrives in time to be applied.
379-
if (initialRoute != null) {
380-
flutterEngine.getNavigationChannel().setInitialRoute(initialRoute);
381-
}
383+
flutterEngine.getNavigationChannel().setInitialRoute(initialRoute);
382384

383385
String appBundlePathOverride = host.getAppBundlePath();
384386
if (appBundlePathOverride == null || appBundlePathOverride.isEmpty()) {
@@ -753,11 +755,7 @@ private void ensureAlive() {
753755
@NonNull
754756
Context getContext();
755757

756-
/**
757-
* Returns true if the {@link FlutterActivityAndFragmentDelegate} should send the deeplinking
758-
* URL to the framework through the {@code NavigationChannel.setInitialRoute} or {@code
759-
* NavigationChannel.pushRoute}.
760-
*/
758+
/** Returns true if the delegate should retrieve the initial route from the {@link Intent}. */
761759
@Nullable
762760
boolean shouldHandleDeeplinking();
763761

shell/platform/android/io/flutter/embedding/android/FlutterActivityLaunchConfigs.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ public class FlutterActivityLaunchConfigs {
1616
"io.flutter.embedding.android.SplashScreenDrawable";
1717
/* package */ static final String NORMAL_THEME_META_DATA_KEY =
1818
"io.flutter.embedding.android.NormalTheme";
19-
/* package */ static final String HANDLE_DEEPLINKING_META_DATA_KEY = "flutter_handle_deeplinking";
19+
/* package */ static final String HANDLE_DEEPLINKING_META_DATA_KEY =
20+
"flutter_deeplinking_enabled";
2021
// Intent extra arguments.
2122
/* package */ static final String EXTRA_INITIAL_ROUTE = "route";
2223
/* package */ static final String EXTRA_BACKGROUND_MODE = "background_mode";

shell/platform/android/io/flutter/embedding/android/FlutterFragment.java

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,10 @@ public NewEngineFragmentBuilder initialRoute(@NonNull String initialRoute) {
227227
return this;
228228
}
229229

230-
/** Whether the activity delegate should handle the deeplinking request. */
230+
/**
231+
* Whether to handle the deeplinking from the {@code Intent} automatically if the {@code
232+
* getInitialRoute} returns null.
233+
*/
231234
@NonNull
232235
public NewEngineFragmentBuilder handleDeeplinking(@NonNull Boolean handleDeeplinking) {
233236
this.handleDeeplinking = handleDeeplinking;
@@ -472,7 +475,10 @@ public CachedEngineFragmentBuilder transparencyMode(
472475
return this;
473476
}
474477

475-
/** Whether the activity delegate should handle the deeplinking request. */
478+
/**
479+
* Whether to handle the deeplinking from the {@code Intent} automatically if the {@code
480+
* getInitialRoute} returns null.
481+
*/
476482
@NonNull
477483
public CachedEngineFragmentBuilder handleDeeplinking(@NonNull Boolean handleDeeplinking) {
478484
this.handleDeeplinking = handleDeeplinking;
@@ -1037,10 +1043,8 @@ public boolean shouldAttachEngineToActivity() {
10371043
}
10381044

10391045
/**
1040-
* See {@link NewEngineFragmentBuilder#shouldHandleDeeplinking()} and {@link
1041-
* CachedEngineFragmentBuilder#shouldHandleDeeplinking()}.
1042-
*
1043-
* <p>Used by this {@code FlutterFragment}'s {@link FlutterActivityAndFragmentDelegate}
1046+
* Whether to handle the deeplinking from the {@code Intent} automatically if the {@code
1047+
* getInitialRoute} returns null.
10441048
*/
10451049
@Override
10461050
public boolean shouldHandleDeeplinking() {

shell/platform/android/io/flutter/embedding/android/FlutterFragmentActivity.java

Lines changed: 37 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import android.widget.FrameLayout;
3535
import androidx.annotation.NonNull;
3636
import androidx.annotation.Nullable;
37+
import androidx.annotation.VisibleForTesting;
3738
import androidx.fragment.app.FragmentActivity;
3839
import androidx.fragment.app.FragmentManager;
3940
import io.flutter.Log;
@@ -280,10 +281,9 @@ protected void onCreate(@Nullable Bundle savedInstanceState) {
280281
*/
281282
private void switchLaunchThemeForNormalTheme() {
282283
try {
283-
ActivityInfo activityInfo =
284-
getPackageManager().getActivityInfo(getComponentName(), PackageManager.GET_META_DATA);
285-
if (activityInfo.metaData != null) {
286-
int normalThemeRID = activityInfo.metaData.getInt(NORMAL_THEME_META_DATA_KEY, -1);
284+
Bundle metaData = getMetaData();
285+
if (metaData != null) {
286+
int normalThemeRID = metaData.getInt(NORMAL_THEME_META_DATA_KEY, -1);
287287
if (normalThemeRID != -1) {
288288
setTheme(normalThemeRID);
289289
}
@@ -319,9 +319,7 @@ public SplashScreen provideSplashScreen() {
319319
@SuppressWarnings("deprecation")
320320
private Drawable getSplashScreenFromManifest() {
321321
try {
322-
ActivityInfo activityInfo =
323-
getPackageManager().getActivityInfo(getComponentName(), PackageManager.GET_META_DATA);
324-
Bundle metadata = activityInfo.metaData;
322+
Bundle metadata = getMetaData();
325323
Integer splashScreenId =
326324
metadata != null ? metadata.getInt(SPLASH_SCREEN_META_DATA_KEY) : null;
327325
return splashScreenId != null
@@ -548,11 +546,17 @@ protected boolean shouldAttachEngineToActivity() {
548546
return true;
549547
}
550548

549+
/**
550+
* Whether to handle the deeplinking from the {@code Intent} automatically if the {@code
551+
* getInitialRoute} returns null.
552+
*
553+
* <p>The default implementation looks for the value of the key `flutter_handle_deeplinking` in
554+
* the AndroidManifest.xml.
555+
*/
556+
@VisibleForTesting
551557
protected boolean shouldHandleDeeplinking() {
552558
try {
553-
ActivityInfo activityInfo =
554-
getPackageManager().getActivityInfo(getComponentName(), PackageManager.GET_META_DATA);
555-
Bundle metadata = activityInfo.metaData;
559+
Bundle metadata = getMetaData();
556560
boolean shouldHandleDeeplinking =
557561
metadata != null ? metadata.getBoolean(HANDLE_DEEPLINKING_META_DATA_KEY) : false;
558562
return shouldHandleDeeplinking;
@@ -624,6 +628,25 @@ protected String getAppBundlePath() {
624628
return null;
625629
}
626630

631+
private Bundle cachedMetaData;
632+
633+
/** Mocks the meta data for testing purposes. */
634+
@VisibleForTesting
635+
public void setMetaData(Bundle metaData) {
636+
cachedMetaData = metaData;
637+
};
638+
639+
/** Retrieves the meta data specified in the AndroidManifest.xml. */
640+
@Nullable
641+
protected Bundle getMetaData() throws PackageManager.NameNotFoundException {
642+
if (cachedMetaData == null) {
643+
ActivityInfo activityInfo =
644+
getPackageManager().getActivityInfo(getComponentName(), PackageManager.GET_META_DATA);
645+
cachedMetaData = activityInfo.metaData;
646+
}
647+
return cachedMetaData;
648+
}
649+
627650
/**
628651
* The Dart entrypoint that will be executed as soon as the Dart snapshot is loaded.
629652
*
@@ -636,9 +659,7 @@ protected String getAppBundlePath() {
636659
@NonNull
637660
public String getDartEntrypointFunctionName() {
638661
try {
639-
ActivityInfo activityInfo =
640-
getPackageManager().getActivityInfo(getComponentName(), PackageManager.GET_META_DATA);
641-
Bundle metadata = activityInfo.metaData;
662+
Bundle metadata = getMetaData();
642663
String desiredDartEntrypoint =
643664
metadata != null ? metadata.getString(DART_ENTRYPOINT_META_DATA_KEY) : null;
644665
return desiredDartEntrypoint != null ? desiredDartEntrypoint : DEFAULT_DART_ENTRYPOINT;
@@ -668,19 +689,16 @@ public String getDartEntrypointFunctionName() {
668689
*
669690
* <p>Subclasses may override this method to directly control the initial route.
670691
*
671-
* <p>If this method returns null and the {@code shouldHandleDeeplinking()} returns true, the
672-
* {@link FlutterActivityAndFragmentDelegate} retrieves the initial route from the {@code Intent}
673-
* through the Intent.getData() instead.
692+
* <p>If this method returns null and the {@code shouldHandleDeeplinking} returns true, the
693+
* initial route is derived from the {@code Intent} through the Intent.getData() instead.
674694
*/
675695
protected String getInitialRoute() {
676696
if (getIntent().hasExtra(EXTRA_INITIAL_ROUTE)) {
677697
return getIntent().getStringExtra(EXTRA_INITIAL_ROUTE);
678698
}
679699

680700
try {
681-
ActivityInfo activityInfo =
682-
getPackageManager().getActivityInfo(getComponentName(), PackageManager.GET_META_DATA);
683-
Bundle metadata = activityInfo.metaData;
701+
Bundle metadata = getMetaData();
684702
String desiredInitialRoute =
685703
metadata != null ? metadata.getString(INITIAL_ROUTE_META_DATA_KEY) : null;
686704
return desiredInitialRoute;

shell/platform/android/test/io/flutter/embedding/android/FlutterActivityAndFragmentDelegateTest.java

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -430,7 +430,8 @@ public void itForwardsOnRequestPermissionsResultToFlutterEngine() {
430430
}
431431

432432
@Test
433-
public void itSendsInitialRouteFromIntentOnStartIfNoInitialRouteFromActivity() {
433+
public void
434+
itSendsInitialRouteFromIntentOnStartIfNoInitialRouteFromActivityAndShouldHandleDeeplinking() {
434435
Intent intent = FlutterActivity.createDefaultIntent(RuntimeEnvironment.application);
435436
intent.setData(Uri.parse("http://myApp/custom/route"));
436437

@@ -455,6 +456,31 @@ public void itSendsInitialRouteFromIntentOnStartIfNoInitialRouteFromActivity() {
455456
.setInitialRoute("http://myApp/custom/route");
456457
}
457458

459+
@Test
460+
public void itSendsdefaultInitialRouteOnStartIfNotDeepLinkingFromIntent() {
461+
// Creates an empty intent without launch uri.
462+
Intent intent = FlutterActivity.createDefaultIntent(RuntimeEnvironment.application);
463+
464+
ActivityController<FlutterActivity> activityController =
465+
Robolectric.buildActivity(FlutterActivity.class, intent);
466+
FlutterActivity flutterActivity = activityController.get();
467+
468+
when(mockHost.getActivity()).thenReturn(flutterActivity);
469+
when(mockHost.getInitialRoute()).thenReturn(null);
470+
when(mockHost.shouldHandleDeeplinking()).thenReturn(true);
471+
// Create the real object that we're testing.
472+
FlutterActivityAndFragmentDelegate delegate = new FlutterActivityAndFragmentDelegate(mockHost);
473+
474+
// --- Execute the behavior under test ---
475+
// The FlutterEngine is setup in onAttach().
476+
delegate.onAttach(RuntimeEnvironment.application);
477+
// Emulate app start.
478+
delegate.onStart();
479+
480+
// Verify that the navigation channel was given the default initial route message.
481+
verify(mockFlutterEngine.getNavigationChannel(), times(1)).setInitialRoute("/");
482+
}
483+
458484
@Test
459485
public void itSendsPushRouteMessageWhenOnNewIntent() {
460486
when(mockHost.shouldHandleDeeplinking()).thenReturn(true);

0 commit comments

Comments
 (0)