未验证 提交 742dfbef 编写于 作者: C chunhtai 提交者: GitHub

support uri intent launcher in android (#21275)

* support uri intent launcher in android

* fix comment
上级 3d27fd5b
......@@ -774,13 +774,18 @@ public class FlutterActivity extends Activity
*
* If both preferences are set, the {@code Intent} preference takes priority.
*
* <p>If none is set, the {@link FlutterActivityAndFragmentDelegate} retrieves the initial route
* from the {@code Intent} through the Intent.getData() instead.
*
* <p>The reason that a {@code <meta-data>} preference is supported is because this {@code
* Activity} might be the very first {@code Activity} launched, which means the developer won't
* have control over the incoming {@code Intent}.
*
* <p>Subclasses may override this method to directly control the initial route.
*
* <p>If this method returns null, the {@link FlutterActivityAndFragmentDelegate} retrieves the
* initial route from the {@code Intent} through the Intent.getData() instead.
*/
@NonNull
public String getInitialRoute() {
if (getIntent().hasExtra(EXTRA_INITIAL_ROUTE)) {
return getIntent().getStringExtra(EXTRA_INITIAL_ROUTE);
......@@ -792,9 +797,9 @@ public class FlutterActivity extends Activity
Bundle metadata = activityInfo.metaData;
String desiredInitialRoute =
metadata != null ? metadata.getString(INITIAL_ROUTE_META_DATA_KEY) : null;
return desiredInitialRoute != null ? desiredInitialRoute : DEFAULT_INITIAL_ROUTE;
return desiredInitialRoute;
} catch (PackageManager.NameNotFoundException e) {
return DEFAULT_INITIAL_ROUTE;
return null;
}
}
......
......@@ -9,6 +9,7 @@ import static android.content.ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.view.LayoutInflater;
......@@ -362,18 +363,21 @@ import java.util.Arrays;
// So this is expected behavior in many cases.
return;
}
String initialRoute = host.getInitialRoute();
if (initialRoute == null) {
initialRoute = getInitialRouteFromIntent(host.getActivity().getIntent());
}
Log.v(
TAG,
"Executing Dart entrypoint: "
+ host.getDartEntrypointFunctionName()
+ ", and sending initial route: "
+ host.getInitialRoute());
+ initialRoute);
// The engine needs to receive the Flutter app's initial route before executing any
// Dart code to ensure that the initial route arrives in time to be applied.
if (host.getInitialRoute() != null) {
flutterEngine.getNavigationChannel().setInitialRoute(host.getInitialRoute());
if (initialRoute != null) {
flutterEngine.getNavigationChannel().setInitialRoute(initialRoute);
}
String appBundlePathOverride = host.getAppBundlePath();
......@@ -388,6 +392,14 @@ import java.util.Arrays;
flutterEngine.getDartExecutor().executeDartEntrypoint(entrypoint);
}
private String getInitialRouteFromIntent(Intent intent) {
Uri data = intent.getData();
if (data != null && !data.toString().isEmpty()) {
return data.toString();
}
return null;
}
/**
* Invoke this from {@code Activity#onResume()} or {@code Fragment#onResume()}.
*
......@@ -622,8 +634,12 @@ import java.util.Arrays;
void onNewIntent(@NonNull Intent intent) {
ensureAlive();
if (flutterEngine != null) {
Log.v(TAG, "Forwarding onNewIntent() to FlutterEngine.");
Log.v(TAG, "Forwarding onNewIntent() to FlutterEngine and sending pushRoute message.");
flutterEngine.getActivityControlSurface().onNewIntent(intent);
String initialRoute = getInitialRouteFromIntent(intent);
if (initialRoute != null && !initialRoute.isEmpty()) {
flutterEngine.getNavigationChannel().pushRoute(initialRoute);
}
} else {
Log.w(TAG, "onNewIntent() invoked before FlutterFragment was attached to an Activity.");
}
......
......@@ -646,13 +646,18 @@ public class FlutterFragmentActivity extends FragmentActivity
*
* If both preferences are set, the {@code Intent} preference takes priority.
*
* <p>If none is set, the {@link FlutterActivityAndFragmentDelegate} retrieves the initial route
* from the {@code Intent} through the Intent.getData() instead.
*
* <p>The reason that a {@code <meta-data>} preference is supported is because this {@code
* Activity} might be the very first {@code Activity} launched, which means the developer won't
* have control over the incoming {@code Intent}.
*
* <p>Subclasses may override this method to directly control the initial route.
*
* <p>If this method returns null, the {@link FlutterActivityAndFragmentDelegate} retrieves the
* initial route from the {@code Intent} through the Intent.getData() instead.
*/
@NonNull
protected String getInitialRoute() {
if (getIntent().hasExtra(EXTRA_INITIAL_ROUTE)) {
return getIntent().getStringExtra(EXTRA_INITIAL_ROUTE);
......@@ -664,9 +669,9 @@ public class FlutterFragmentActivity extends FragmentActivity
Bundle metadata = activityInfo.metaData;
String desiredInitialRoute =
metadata != null ? metadata.getString(INITIAL_ROUTE_META_DATA_KEY) : null;
return desiredInitialRoute != null ? desiredInitialRoute : DEFAULT_INITIAL_ROUTE;
return desiredInitialRoute;
} catch (PackageManager.NameNotFoundException e) {
return DEFAULT_INITIAL_ROUTE;
return null;
}
}
......
......@@ -15,6 +15,7 @@ import static org.mockito.Mockito.when;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import androidx.annotation.NonNull;
import androidx.lifecycle.Lifecycle;
import io.flutter.FlutterInjector;
......@@ -43,6 +44,7 @@ import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.android.controller.ActivityController;
import org.robolectric.annotation.Config;
@Config(manifest = Config.NONE)
......@@ -426,6 +428,50 @@ public class FlutterActivityAndFragmentDelegateTest {
.onRequestPermissionsResult(any(Integer.class), any(String[].class), any(int[].class));
}
@Test
public void itSendsInitialRouteFromIntentOnStartIfnoInitialRouteFromActivity() {
Intent intent = FlutterActivity.createDefaultIntent(RuntimeEnvironment.application);
intent.setData(Uri.parse("http://myApp/custom/route"));
ActivityController<FlutterActivity> activityController =
Robolectric.buildActivity(FlutterActivity.class, intent);
FlutterActivity flutterActivity = activityController.get();
when(mockHost.getActivity()).thenReturn(flutterActivity);
when(mockHost.getInitialRoute()).thenReturn(null);
// Create the real object that we're testing.
FlutterActivityAndFragmentDelegate delegate = new FlutterActivityAndFragmentDelegate(mockHost);
// --- Execute the behavior under test ---
// The FlutterEngine is setup in onAttach().
delegate.onAttach(RuntimeEnvironment.application);
// Emulate app start.
delegate.onStart();
// Verify that the navigation channel was given the initial route message.
verify(mockFlutterEngine.getNavigationChannel(), times(1))
.setInitialRoute("http://myApp/custom/route");
}
@Test
public void itSendsPushRouteMessageWhenOnNewIntent() {
// Create the real object that we're testing.
FlutterActivityAndFragmentDelegate delegate = new FlutterActivityAndFragmentDelegate(mockHost);
// --- Execute the behavior under test ---
// The FlutterEngine is setup in onAttach().
delegate.onAttach(RuntimeEnvironment.application);
Intent mockIntent = mock(Intent.class);
when(mockIntent.getData()).thenReturn(Uri.parse("http://myApp/custom/route"));
// Emulate the host and call the method that we expect to be forwarded.
delegate.onNewIntent(mockIntent);
// Verify that the navigation channel was given the push route message.
verify(mockFlutterEngine.getNavigationChannel(), times(1))
.pushRoute("http://myApp/custom/route");
}
@Test
public void itForwardsOnNewIntentToFlutterEngine() {
// Create the real object that we're testing.
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册