Commit eab0fc97 authored by Pavel Shmakov's avatar Pavel Shmakov Committed by Commit Bot

Deliver intents to active Custom Tabs via onNewIntent

Currently the intents to be handled in the foreground instance of
CustomTabActivity are delivered via a static reference in
BrowserSessionContentUtils, instead of delivering the intent via
ordinary Android mechanisms. This means, in particular, that intent
flags are lost in the process.

This poses a problem for "Single activity" TWA apps, specifically for
their ability to forward new incoming View intents to the running TWA.
We'd like this forwarding to be possible, and to work "out of the box"
with LauncherActivity.

Currently it works as follows. LauncherActivity starts a TWA, and keeps
running in background - this allows to customize it so that it would
continue communicating with TWA via CustomTabConnection. If a new
View intent comes in, it starts a new instance of LauncherActivity,
which in turn creates ChromeLauncherActivity, so the stack is:

LauncherActivity | CTA | LauncherActivity | ChromeLauncherActivity

ChromeLauncherActivity calls CTA via a static method, and kills itself,
which leaves the second LauncherActivity running. Setting SINGLE_TOP and
CLEAR_TOP intent flags on the client won't help, because these flags won't
reach CTA.

In this CL I make ChromeLauncherActivity launch CTA with SINGLE_TOP and
CLEAR_TOP if there is a foreground CTA with matching session. Otherwise,
the intent handling is kept the way it was, with all the logic untouched.

The fallback solution, in the case this one won't work out, is to just
call  finish() in LauncherActivity once it has launched a TWA. This would
mean it won't be able to both do the intent routing and keep communicating
via CustomTabConnection (and probably no client implementation would be
able to), which isn't good, but might not be a blocker for MVP release.

Companion CL: http://crrev.com/c/1349363

Bug: 907796
Change-Id: I8a2396b325baae2974d6b0dfb73c2ed2fb0519eb
Reviewed-on: https://chromium-review.googlesource.com/c/1349653
Commit-Queue: Pavel Shmakov <pshmakov@chromium.org>
Reviewed-by: default avatarYusuf Ozuysal <yusufo@chromium.org>
Reviewed-by: default avatarPeter Conn <peconn@chromium.org>
Cr-Commit-Position: refs/heads/master@{#611375}
parent 3d7014fa
...@@ -671,7 +671,7 @@ deps = { ...@@ -671,7 +671,7 @@ deps = {
}, },
'src/third_party/custom_tabs_client/src': { 'src/third_party/custom_tabs_client/src': {
'url': Var('chromium_git') + '/custom-tabs-client.git' + '@' + '23a570c65c45ad19725b53359c2556156b9a4363', 'url': Var('chromium_git') + '/custom-tabs-client.git' + '@' + 'b61484e1cf0e849fe2f1c2271effa7748efc7db6',
'condition': 'checkout_android', 'condition': 'checkout_android',
}, },
......
...@@ -17,6 +17,7 @@ import android.os.Bundle; ...@@ -17,6 +17,7 @@ import android.os.Bundle;
import android.os.StrictMode; import android.os.StrictMode;
import android.support.annotation.IntDef; import android.support.annotation.IntDef;
import android.support.customtabs.CustomTabsIntent; import android.support.customtabs.CustomTabsIntent;
import android.support.customtabs.CustomTabsSessionToken;
import org.chromium.base.ApplicationStatus; import org.chromium.base.ApplicationStatus;
import org.chromium.base.CommandLine; import org.chromium.base.CommandLine;
...@@ -25,9 +26,9 @@ import org.chromium.base.Log; ...@@ -25,9 +26,9 @@ import org.chromium.base.Log;
import org.chromium.base.StrictModeContext; import org.chromium.base.StrictModeContext;
import org.chromium.base.library_loader.LibraryProcessType; import org.chromium.base.library_loader.LibraryProcessType;
import org.chromium.base.metrics.CachedMetrics; import org.chromium.base.metrics.CachedMetrics;
import org.chromium.chrome.browser.browserservices.BrowserSessionContentUtils;
import org.chromium.chrome.browser.customtabs.CustomTabActivity; import org.chromium.chrome.browser.customtabs.CustomTabActivity;
import org.chromium.chrome.browser.customtabs.CustomTabIntentDataProvider; import org.chromium.chrome.browser.customtabs.CustomTabIntentDataProvider;
import org.chromium.chrome.browser.customtabs.CustomTabsConnection;
import org.chromium.chrome.browser.customtabs.PaymentHandlerActivity; import org.chromium.chrome.browser.customtabs.PaymentHandlerActivity;
import org.chromium.chrome.browser.customtabs.SeparateTaskCustomTabActivity; import org.chromium.chrome.browser.customtabs.SeparateTaskCustomTabActivity;
import org.chromium.chrome.browser.firstrun.FirstRunFlowSequencer; import org.chromium.chrome.browser.firstrun.FirstRunFlowSequencer;
...@@ -291,6 +292,15 @@ public class LaunchIntentDispatcher implements IntentHandler.IntentHandlerDelega ...@@ -291,6 +292,15 @@ public class LaunchIntentDispatcher implements IntentHandler.IntentHandlerDelega
newIntent.setData(uri); newIntent.setData(uri);
newIntent.setClassName(context, CustomTabActivity.class.getName()); newIntent.setClassName(context, CustomTabActivity.class.getName());
// Ensure the new intent is routed into the instance of CustomTabActivity in this task, if
// it exists. If the existing CustomTabActivity can't handle the intent, it will re-launch
// the intent without these flags.
// If you change this flow, please make sure it works correctly with
// - "Don't keep activities",
// - Multiple clients hosting CCTs,
// - Multiwindow mode.
newIntent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);
// Use a custom tab with a unique theme for payment handlers. // Use a custom tab with a unique theme for payment handlers.
if (intent.getIntExtra(CustomTabIntentDataProvider.EXTRA_UI_TYPE, if (intent.getIntExtra(CustomTabIntentDataProvider.EXTRA_UI_TYPE,
CustomTabIntentDataProvider.CustomTabsUiType.DEFAULT) CustomTabIntentDataProvider.CustomTabsUiType.DEFAULT)
...@@ -367,10 +377,9 @@ public class LaunchIntentDispatcher implements IntentHandler.IntentHandlerDelega ...@@ -367,10 +377,9 @@ public class LaunchIntentDispatcher implements IntentHandler.IntentHandlerDelega
* in the same task. * in the same task.
*/ */
private void launchCustomTabActivity() { private void launchCustomTabActivity() {
boolean handled = BrowserSessionContentUtils.handleBrowserServicesIntent(mIntent);
if (handled) return;
maybePrefetchDnsInBackground(); maybePrefetchDnsInBackground();
CustomTabsConnection.getInstance().onHandledIntent(
CustomTabsSessionToken.getSessionTokenFromIntent(mIntent), mIntent);
// Create and fire a launch intent. // Create and fire a launch intent.
Intent launchIntent = createCustomTabActivityIntent(mActivity, mIntent); Intent launchIntent = createCustomTabActivityIntent(mActivity, mIntent);
......
...@@ -10,6 +10,7 @@ import android.content.Intent; ...@@ -10,6 +10,7 @@ import android.content.Intent;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.net.Uri; import android.net.Uri;
import android.os.IBinder; import android.os.IBinder;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.customtabs.CustomTabsService; import android.support.customtabs.CustomTabsService;
import android.support.customtabs.CustomTabsSessionToken; import android.support.customtabs.CustomTabsSessionToken;
...@@ -22,7 +23,6 @@ import org.chromium.chrome.browser.ChromeActivity; ...@@ -22,7 +23,6 @@ import org.chromium.chrome.browser.ChromeActivity;
import org.chromium.chrome.browser.IntentHandler; import org.chromium.chrome.browser.IntentHandler;
import org.chromium.chrome.browser.UrlConstants; import org.chromium.chrome.browser.UrlConstants;
import org.chromium.chrome.browser.customtabs.CustomTabsConnection; import org.chromium.chrome.browser.customtabs.CustomTabsConnection;
import org.chromium.chrome.browser.document.ChromeLauncherActivity;
import org.chromium.content_public.browser.LoadUrlParams; import org.chromium.content_public.browser.LoadUrlParams;
/** /**
...@@ -45,10 +45,20 @@ public class BrowserSessionContentUtils { ...@@ -45,10 +45,20 @@ public class BrowserSessionContentUtils {
* Sets the currently active {@link BrowserSessionContentHandler} in focus. * Sets the currently active {@link BrowserSessionContentHandler} in focus.
* @param contentHandler {@link BrowserSessionContentHandler} to set. * @param contentHandler {@link BrowserSessionContentHandler} to set.
*/ */
public static void setActiveContentHandler(BrowserSessionContentHandler contentHandler) { public static void setActiveContentHandler(
@NonNull BrowserSessionContentHandler contentHandler) {
sActiveContentHandler = contentHandler; sActiveContentHandler = contentHandler;
} }
/**
* Notifies that given {@link BrowserSessionContentHandler} no longer has focus.
*/
public static void removeActiveContentHandler(BrowserSessionContentHandler contentHandler) {
if (sActiveContentHandler == contentHandler) {
sActiveContentHandler = null;
} // else this contentHandler has already been replaced.
}
/** /**
* Called when a Browser Services intent is handled. * Called when a Browser Services intent is handled.
* *
...@@ -62,14 +72,8 @@ public class BrowserSessionContentUtils { ...@@ -62,14 +72,8 @@ public class BrowserSessionContentUtils {
if (TextUtils.isEmpty(url)) return false; if (TextUtils.isEmpty(url)) return false;
CustomTabsSessionToken session = CustomTabsSessionToken.getSessionTokenFromIntent(intent); CustomTabsSessionToken session = CustomTabsSessionToken.getSessionTokenFromIntent(intent);
return handleInternalIntent(intent, session) || handleExternalIntent(intent, url, session);
if (handleInternalIntent(intent, session)) return true;
// Must be called regardless of whether or not an external intent can be handled in active
// content.
CustomTabsConnection.getInstance().onHandledIntent(session, url, intent);
return handleExternalIntent(intent, url, session);
} }
private static boolean handleInternalIntent(Intent intent, private static boolean handleInternalIntent(Intent intent,
...@@ -195,9 +199,7 @@ public class BrowserSessionContentUtils { ...@@ -195,9 +199,7 @@ public class BrowserSessionContentUtils {
*/ */
public static Intent createShareIntent(Context context, Intent originalIntent) { public static Intent createShareIntent(Context context, Intent originalIntent) {
Intent intent = new Intent(originalIntent) Intent intent = new Intent(originalIntent)
.putExtra(EXTRA_INTERNAL_ACTION, INTERNAL_ACTION_SHARE) .putExtra(EXTRA_INTERNAL_ACTION, INTERNAL_ACTION_SHARE);
// Make the new intent follow the same route as the original one
.setClass(context, ChromeLauncherActivity.class);
IntentHandler.addTrustedIntentExtras(intent); IntentHandler.addTrustedIntentExtras(intent);
return intent; return intent;
} }
......
...@@ -765,6 +765,26 @@ public class CustomTabActivity extends ChromeActivity<CustomTabActivityComponent ...@@ -765,6 +765,26 @@ public class CustomTabActivity extends ChromeActivity<CustomTabActivityComponent
super.finishNativeInitialization(); super.finishNativeInitialization();
} }
@Override
protected void onNewIntent(Intent intent) {
Intent originalIntent = getIntent();
super.onNewIntent(intent);
// Currently we can't handle arbitrary updates of intent parameters, so make sure
// getIntent() returns the same intent as before.
setIntent(originalIntent);
}
@Override
public void onNewIntentWithNative(Intent intent) {
super.onNewIntentWithNative(intent);
BrowserSessionContentUtils.setActiveContentHandler(mBrowserSessionContentHandler);
if (!BrowserSessionContentUtils.handleBrowserServicesIntent(intent)) {
int flagsToRemove = Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP;
intent.setFlags(intent.getFlags() & ~flagsToRemove);
startActivity(intent);
}
}
/** /**
* Encapsulates CustomTabsConnection#takeHiddenTab() * Encapsulates CustomTabsConnection#takeHiddenTab()
* with additional initialization logic. * with additional initialization logic.
...@@ -1044,7 +1064,7 @@ public class CustomTabActivity extends ChromeActivity<CustomTabActivityComponent ...@@ -1044,7 +1064,7 @@ public class CustomTabActivity extends ChromeActivity<CustomTabActivityComponent
@Override @Override
public void onStopWithNative() { public void onStopWithNative() {
super.onStopWithNative(); super.onStopWithNative();
BrowserSessionContentUtils.setActiveContentHandler(null); BrowserSessionContentUtils.removeActiveContentHandler(mBrowserSessionContentHandler);
if (mModuleActivityDelegate != null) mModuleActivityDelegate.onStop(); if (mModuleActivityDelegate != null) mModuleActivityDelegate.onStop();
mModuleOnStartPending = false; mModuleOnStartPending = false;
if (mIsClosing) { if (mIsClosing) {
......
...@@ -840,10 +840,13 @@ public class CustomTabsConnection { ...@@ -840,10 +840,13 @@ public class CustomTabsConnection {
* Called when an intent is handled by either an existing or a new CustomTabActivity. * Called when an intent is handled by either an existing or a new CustomTabActivity.
* *
* @param session Session extracted from the intent. * @param session Session extracted from the intent.
* @param url URL extracted from the intent.
* @param intent incoming intent. * @param intent incoming intent.
*/ */
public void onHandledIntent(CustomTabsSessionToken session, String url, Intent intent) { public void onHandledIntent(CustomTabsSessionToken session, Intent intent) {
String url = IntentHandler.getUrlFromIntent(intent);
if (TextUtils.isEmpty(url)) {
return;
}
if (mLogRequests) { if (mLogRequests) {
Log.w(TAG, "onHandledIntent, URL: %s, extras: %s", url, Log.w(TAG, "onHandledIntent, URL: %s, extras: %s", url,
bundleToJson(intent.getExtras())); bundleToJson(intent.getExtras()));
......
...@@ -257,7 +257,7 @@ public class DetachedResourceRequestTest { ...@@ -257,7 +257,7 @@ public class DetachedResourceRequestTest {
CustomTabsSessionToken session = prepareSession(ORIGIN, customTabsCallback); CustomTabsSessionToken session = prepareSession(ORIGIN, customTabsCallback);
ThreadUtils.runOnUiThread( ThreadUtils.runOnUiThread(
() -> mConnection.onHandledIntent(session, "", prepareIntent(url, ORIGIN))); () -> mConnection.onHandledIntent(session, prepareIntent(url, ORIGIN)));
CustomTabsTestUtils.warmUpAndWait(); CustomTabsTestUtils.warmUpAndWait();
customTabsCallback.waitForRequest(); customTabsCallback.waitForRequest();
cb.waitForCallback(0, 1); cb.waitForCallback(0, 1);
...@@ -417,7 +417,7 @@ public class DetachedResourceRequestTest { ...@@ -417,7 +417,7 @@ public class DetachedResourceRequestTest {
if (afterNative) CustomTabsTestUtils.warmUpAndWait(); if (afterNative) CustomTabsTestUtils.warmUpAndWait();
ThreadUtils.runOnUiThread( ThreadUtils.runOnUiThread(
() -> mConnection.onHandledIntent(session, "", prepareIntent(url, ORIGIN))); () -> mConnection.onHandledIntent(session, prepareIntent(url, ORIGIN)));
if (!afterNative) CustomTabsTestUtils.warmUpAndWait(); if (!afterNative) CustomTabsTestUtils.warmUpAndWait();
customTabsCallback.waitForRequest(); customTabsCallback.waitForRequest();
...@@ -436,7 +436,7 @@ public class DetachedResourceRequestTest { ...@@ -436,7 +436,7 @@ public class DetachedResourceRequestTest {
if (afterNative) CustomTabsTestUtils.warmUpAndWait(); if (afterNative) CustomTabsTestUtils.warmUpAndWait();
ThreadUtils.runOnUiThreadBlocking( ThreadUtils.runOnUiThreadBlocking(
() -> mConnection.onHandledIntent(session, "", prepareIntent(url, ORIGIN))); () -> mConnection.onHandledIntent(session, prepareIntent(url, ORIGIN)));
if (!afterNative) CustomTabsTestUtils.warmUpAndWait(); if (!afterNative) CustomTabsTestUtils.warmUpAndWait();
customTabsCallback.waitForRequest(); customTabsCallback.waitForRequest();
...@@ -556,7 +556,7 @@ public class DetachedResourceRequestTest { ...@@ -556,7 +556,7 @@ public class DetachedResourceRequestTest {
if (afterNative) CustomTabsTestUtils.warmUpAndWait(); if (afterNative) CustomTabsTestUtils.warmUpAndWait();
ThreadUtils.runOnUiThreadBlocking( ThreadUtils.runOnUiThreadBlocking(
() -> mConnection.onHandledIntent(session, "", prepareIntent(url, ORIGIN))); () -> mConnection.onHandledIntent(session, prepareIntent(url, ORIGIN)));
if (!afterNative) CustomTabsTestUtils.warmUpAndWait(); if (!afterNative) CustomTabsTestUtils.warmUpAndWait();
readFromSocketCallback.waitForCallback(0); readFromSocketCallback.waitForCallback(0);
return readFromSocketCallback; return readFromSocketCallback;
...@@ -564,6 +564,7 @@ public class DetachedResourceRequestTest { ...@@ -564,6 +564,7 @@ public class DetachedResourceRequestTest {
private static Intent prepareIntent(Uri url, Uri referrer) { private static Intent prepareIntent(Uri url, Uri referrer) {
Intent intent = new Intent(); Intent intent = new Intent();
intent.setData(Uri.parse("http://www.example.com"));
intent.putExtra(CustomTabsConnection.PARALLEL_REQUEST_URL_KEY, url); intent.putExtra(CustomTabsConnection.PARALLEL_REQUEST_URL_KEY, url);
intent.putExtra(CustomTabsConnection.PARALLEL_REQUEST_REFERRER_KEY, referrer); intent.putExtra(CustomTabsConnection.PARALLEL_REQUEST_REFERRER_KEY, referrer);
return intent; return intent;
...@@ -571,6 +572,7 @@ public class DetachedResourceRequestTest { ...@@ -571,6 +572,7 @@ public class DetachedResourceRequestTest {
private static Intent prepareIntentForResourcePrefetch(List<Uri> urls, Uri referrer) { private static Intent prepareIntentForResourcePrefetch(List<Uri> urls, Uri referrer) {
Intent intent = new Intent(); Intent intent = new Intent();
intent.setData(Uri.parse("http://www.example.com"));
intent.putExtra(CustomTabsConnection.RESOURCE_PREFETCH_URL_LIST_KEY, new ArrayList<>(urls)); intent.putExtra(CustomTabsConnection.RESOURCE_PREFETCH_URL_LIST_KEY, new ArrayList<>(urls));
intent.putExtra(CustomTabsConnection.PARALLEL_REQUEST_REFERRER_KEY, referrer); intent.putExtra(CustomTabsConnection.PARALLEL_REQUEST_REFERRER_KEY, referrer);
return intent; return intent;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment