Commit 9385a4e2 authored by Ethan Xu's avatar Ethan Xu Committed by Commit Bot

[Android WebAPK] Update ShellAPK in order to support POST share targets

This CL updates the ShellAPK (but not Chrome) to support POST share targets. In particular, this
CL:
- Adds needed <meta-data> tags to AndroidManifest.xml
- Updates how the start URL is computed for share intents
- Sends activity class name of selected share target to Chrome in launch intent. Chrome needs
  this to know which share target activity to read the <meta-data> from.

BUG=885314

Change-Id: Ie8732a8477d722ebdb81595c602b289fab0c7a27
Reviewed-on: https://chromium-review.googlesource.com/c/1281691
Commit-Queue: Ethan Xu <xuethan@google.com>
Reviewed-by: default avatarPeter Kotwicz <pkotwicz@chromium.org>
Reviewed-by: default avatarGlenn Hartmann <hartmanng@chromium.org>
Cr-Commit-Position: refs/heads/master@{#601686}
parent e5dece50
......@@ -20,6 +20,8 @@ public final class WebApkConstants {
public static final String EXTRA_SOURCE = "org.chromium.chrome.browser.webapp_source";
public static final String EXTRA_WEBAPK_PACKAGE_NAME =
"org.chromium.chrome.browser.webapk_package_name";
public static final String EXTRA_WEBAPK_LAUNCHING_ACTIVITY_CLASS_NAME =
"org.chromium.webapk.launching_activity_class_name";
public static final String EXTRA_FORCE_NAVIGATION =
"org.chromium.chrome.browser.webapk_force_navigation";
// Activity launch time for uma tracking of Chrome web apk startup
......
......@@ -31,7 +31,12 @@ public final class WebApkMetaDataKeys {
public static final String DISTRIBUTOR = "org.chromium.webapk.shell_apk.distributor";
public static final String BADGE_ICON_ID = "org.chromium.webapk.shell_apk.badgeIconId";
public static final String SHARE_ACTION = "org.chromium.webapk.shell_apk.shareAction";
public static final String SHARE_METHOD = "org.chromium.webapk.shell_apk.shareMethod";
public static final String SHARE_ENCTYPE = "org.chromium.webapk.shell_apk.shareEnctype";
public static final String SHARE_PARAM_TITLE = "org.chromium.webapk.shell_apk.shareParamTitle";
public static final String SHARE_PARAM_TEXT = "org.chromium.webapk.shell_apk.shareParamText";
public static final String SHARE_PARAM_URL = "org.chromium.webapk.shell_apk.shareParamUrl";
public static final String SHARE_PARAM_NAMES = "org.chromium.webapk.shell_apk.shareParamNames";
public static final String SHARE_PARAM_ACCEPTS =
"org.chromium.webapk.shell_apk.shareParamAccepts";
}
......@@ -69,6 +69,11 @@
android:label="{{{title}}}"
android:targetActivity="org.chromium.webapk.shell_apk.TransparentHostBrowserLauncherActivity">
<meta-data android:name="org.chromium.webapk.shell_apk.shareAction" android:value="{{{action}}}" />
<meta-data android:name="org.chromium.webapk.shell_apk.shareMethod" android:value="{{{method}}}" />
{{#enctype}}
<meta-data android:name="org.chromium.webapk.shell_apk.shareEnctype" android:value="{{{enctype}}}" />
{{/enctype}}
{{^is_file_upload}}
<meta-data android:name="org.chromium.webapk.shell_apk.shareParamTitle" android:value="{{{param_title}}}" />
<meta-data android:name="org.chromium.webapk.shell_apk.shareParamText" android:value="{{{param_text}}}" />
<meta-data android:name="org.chromium.webapk.shell_apk.shareParamUrl" android:value="{{{param_url}}}" />
......@@ -77,6 +82,19 @@
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>
{{/is_file_upload}}
{{#is_file_upload}}
<meta-data android:name="org.chromium.webapk.shell_apk.shareParamNames" android:value="{{{param_names}}}" />
<meta-data android:name="org.chromium.webapk.shell_apk.shareParamAccepts" android:value="{{{param_accepts}}}" />
<intent-filter>
<action android:name="android.intent.action.SEND" />
<action android:name="android.intent.action.SEND_MULTIPLE" />
<category android:name="android.intent.category.DEFAULT" />
{{#mime_types}}
<data android:mimeType="{{{mime_type}}}" />
{{/mime_types}}
</intent-filter>
{{/is_file_upload}}
</activity-alias>
{{/share_template}}
<meta-data android:name="org.chromium.webapk.shell_apk.shellApkVersion" android:value="{{{shell_apk_version}}}" />
......
......@@ -4,6 +4,8 @@
package org.chromium.webapk.shell_apk;
import android.content.Intent;
import android.os.Bundle;
import android.util.Pair;
import org.junit.Assert;
......@@ -12,6 +14,7 @@ import org.junit.runner.RunWith;
import org.robolectric.annotation.Config;
import org.chromium.testing.local.LocalRobolectricTestRunner;
import org.chromium.webapk.lib.common.WebApkMetaDataKeys;
import java.util.ArrayList;
......@@ -22,7 +25,7 @@ import java.util.ArrayList;
@Config(manifest = Config.NONE)
public class HostBrowserLauncherParamsTest {
/*
* Test that {@link HostBrowserLauncherParams#createWebShareTargetUriString()} handles adding
* Test that {@link HostBrowserLauncherParams#createGETWebShareTargetUriString()} handles adding
* query parameters to an action url with different path formats.
*/
@Test
......@@ -31,26 +34,26 @@ public class HostBrowserLauncherParamsTest {
params.add(new Pair<>("title", "mytitle"));
params.add(new Pair<>("foo", "bar"));
String uri = HostBrowserLauncherParams.createWebShareTargetUriString(
String uri = HostBrowserLauncherParams.createGETWebShareTargetUriString(
"https://www.chromium.org/wst", params);
Assert.assertEquals("https://www.chromium.org/wst?title=mytitle&foo=bar", uri);
uri = HostBrowserLauncherParams.createWebShareTargetUriString(
uri = HostBrowserLauncherParams.createGETWebShareTargetUriString(
"https://www.chromium.org/wst/", params);
Assert.assertEquals("https://www.chromium.org/wst/?title=mytitle&foo=bar", uri);
uri = HostBrowserLauncherParams.createWebShareTargetUriString(
uri = HostBrowserLauncherParams.createGETWebShareTargetUriString(
"https://www.chromium.org/base/wst.html", params);
Assert.assertEquals("https://www.chromium.org/base/wst.html?title=mytitle&foo=bar", uri);
uri = HostBrowserLauncherParams.createWebShareTargetUriString(
uri = HostBrowserLauncherParams.createGETWebShareTargetUriString(
"https://www.chromium.org/base/wst.html/", params);
Assert.assertEquals("https://www.chromium.org/base/wst.html/?title=mytitle&foo=bar", uri);
}
/*
* Test that {@link HostBrowserLauncherParams#createWebShareTargetUriString()} skips null names
* or values.
* Test that {@link HostBrowserLauncherParams#createGETWebShareTargetUriString()} skips null
* names or values.
*/
@Test
public void testCreateWebShareTargetUriStringNull() {
......@@ -61,13 +64,13 @@ public class HostBrowserLauncherParamsTest {
params.add(new Pair<>(null, null));
// The baseUrl, shareAction and params are checked to be non-null in
// HostBrowserLauncherParams#extractShareTarget.
String uri = HostBrowserLauncherParams.createWebShareTargetUriString(
String uri = HostBrowserLauncherParams.createGETWebShareTargetUriString(
"https://www.chromium.org/wst", params);
Assert.assertEquals("https://www.chromium.org/wst?hello=world", uri);
}
/*
* Test that {@link HostBrowserLauncherParams#createWebShareTargetUriString()} can handle
* Test that {@link HostBrowserLauncherParams#createGETWebShareTargetUriString()} can handle
* the case where both values in every pair are null.
*/
@Test
......@@ -75,13 +78,13 @@ public class HostBrowserLauncherParamsTest {
ArrayList<Pair<String, String>> params = new ArrayList<>();
params.add(new Pair<>(null, null));
params.add(new Pair<>(null, null));
String uri = HostBrowserLauncherParams.createWebShareTargetUriString(
String uri = HostBrowserLauncherParams.createGETWebShareTargetUriString(
"https://www.chromium.org/wst", params);
Assert.assertEquals("https://www.chromium.org/wst", uri);
}
/*
* Test that {@link HostBrowserLauncherParams#createWebShareTargetUriString()} can handle
* Test that {@link HostBrowserLauncherParams#createGETWebShareTargetUriString()} can handle
* the cases where some keys in all pair are null.
*/
@Test
......@@ -89,27 +92,27 @@ public class HostBrowserLauncherParamsTest {
ArrayList<Pair<String, String>> params = new ArrayList<>();
params.add(new Pair<>("a", null));
params.add(new Pair<>(null, "b"));
String uri = HostBrowserLauncherParams.createWebShareTargetUriString(
String uri = HostBrowserLauncherParams.createGETWebShareTargetUriString(
"https://www.chromium.org/wst", params);
Assert.assertEquals("https://www.chromium.org/wst", uri);
}
/*
* Test that {@link HostBrowserLauncherParams#createWebShareTargetUriString()} handles replacing
* the query string of an action url with an existing query.
* Test that {@link HostBrowserLauncherParams#createGETWebShareTargetUriString()} handles
* replacing the query string of an action url with an existing query.
*/
@Test
public void testCreateWebShareTargetClearQuery() {
ArrayList<Pair<String, String>> params = new ArrayList<>();
params.add(new Pair<>("hello", "world"));
params.add(new Pair<>("foobar", "baz"));
String uri = HostBrowserLauncherParams.createWebShareTargetUriString(
String uri = HostBrowserLauncherParams.createGETWebShareTargetUriString(
"https://www.chromium.org/wst?a=b&c=d", params);
Assert.assertEquals("https://www.chromium.org/wst?hello=world&foobar=baz", uri);
}
/*
* Test that {@link HostBrowserLauncherParams#createWebShareTargetUriString()} escapes
* Test that {@link HostBrowserLauncherParams#createGETWebShareTargetUriString()} escapes
* characters.
*/
@Test
......@@ -118,10 +121,37 @@ public class HostBrowserLauncherParamsTest {
params.add(new Pair<>("hello", "world !\"#$%&'()*+,-./0?@[\\]^_a`{}~"));
params.add(new Pair<>("foo bar", "baz"));
params.add(new Pair<>("a!\"#$%&'()*+,-./0?@[\\]^_a`{}~", "b"));
String uri = HostBrowserLauncherParams.createWebShareTargetUriString(
String uri = HostBrowserLauncherParams.createGETWebShareTargetUriString(
"https://www.chromium.org/wst%25%20space", params);
Assert.assertEquals(
"https://www.chromium.org/wst%25%20space?hello=world+!%22%23%24%25%26'()*%2B%2C-.%2F0%3F%40%5B%5C%5D%5E_a%60%7B%7D~&foo+bar=baz&a!%22%23%24%25%26'()*%2B%2C-.%2F0%3F%40%5B%5C%5D%5E_a%60%7B%7D~=b",
uri);
}
/*
* Test that {@link HostBrowserLauncherParams#computeStartUrlForShareTarget()} computes the
* correct start url for both GET and POST method.
*/
@Test
public void testComputeStartUrlForShareTargetMethod() {
Intent intent = new Intent();
Bundle metaData = new Bundle();
String shareAction = "https://www.chromium.org/wst";
metaData.putString(WebApkMetaDataKeys.SHARE_ACTION, shareAction);
metaData.putString(WebApkMetaDataKeys.SHARE_METHOD, "GET");
metaData.putString(WebApkMetaDataKeys.SHARE_PARAM_TITLE, "hello");
intent.putExtra(Intent.EXTRA_SUBJECT, "world");
String getUri =
HostBrowserLauncherParams.computeStartUrlForShareTarget(metaData, intent);
metaData.putString(WebApkMetaDataKeys.SHARE_METHOD, "POST");
String postUri =
HostBrowserLauncherParams.computeStartUrlForShareTarget(metaData, intent);
Assert.assertEquals("https://www.chromium.org/wst?hello=world", getUri);
Assert.assertEquals(shareAction, postUri);
}
}
......@@ -53,6 +53,8 @@ public class HostBrowserLauncher {
intent.putExtra(WebApkConstants.EXTRA_URL, params.getStartUrl())
.putExtra(WebApkConstants.EXTRA_SOURCE, params.getSource())
.putExtra(WebApkConstants.EXTRA_WEBAPK_PACKAGE_NAME, context.getPackageName())
.putExtra(WebApkConstants.EXTRA_WEBAPK_LAUNCHING_ACTIVITY_CLASS_NAME,
params.getLaunchingActivityClassName())
.putExtra(WebApkConstants.EXTRA_FORCE_NAVIGATION, params.getForceNavigation());
// Only pass on the start time if no user action was required between launching the webapk
......
......@@ -18,6 +18,7 @@ import org.chromium.webapk.lib.common.WebApkConstants;
import org.chromium.webapk.lib.common.WebApkMetaDataKeys;
import java.util.ArrayList;
import java.util.Locale;
/** Convenience wrapper for parameters to {@link HostBrowserLauncher} methods. */
public class HostBrowserLauncherParams {
......@@ -29,6 +30,7 @@ public class HostBrowserLauncherParams {
private int mSource;
private boolean mForceNavigation;
private long mLaunchTimeMs;
private String mLaunchingActivityClassName;
/**
* Constructs a HostBrowserLauncherParams object from the passed in Intent and from <meta-data>
......@@ -45,8 +47,12 @@ public class HostBrowserLauncherParams {
String startUrl = null;
int source = WebApkConstants.SHORTCUT_SOURCE_UNKNOWN;
boolean forceNavigation = false;
if (Intent.ACTION_SEND.equals(intent.getAction())) {
startUrl = computeStartUrlForShareTarget(context, callingActivityComponentName, intent);
if (Intent.ACTION_SEND.equals(intent.getAction())
|| Intent.ACTION_SEND_MULTIPLE.equals(intent.getAction())) {
Bundle shareTargetMetaData =
fetchActivityMetaData(context, callingActivityComponentName);
startUrl = computeStartUrlForShareTarget(shareTargetMetaData, intent);
source = WebApkConstants.SHORTCUT_SOURCE_SHARE;
forceNavigation = true;
} else if (!TextUtils.isEmpty(intent.getDataString())) {
......@@ -69,12 +75,11 @@ public class HostBrowserLauncherParams {
return new HostBrowserLauncherParams(hostBrowserPackageName,
hostBrowserMajorChromiumVersion, dialogShown, intent, startUrl, source,
forceNavigation, launchTimeMs);
forceNavigation, launchTimeMs, callingActivityComponentName.getClassName());
}
/** Computes the start URL for the given share intent and share activity. */
private static String computeStartUrlForShareTarget(
Context context, ComponentName shareTargetComponentName, Intent intent) {
private static Bundle fetchActivityMetaData(
Context context, ComponentName shareTargetComponentName) {
ActivityInfo shareActivityInfo;
try {
shareActivityInfo = context.getPackageManager().getActivityInfo(
......@@ -82,28 +87,63 @@ public class HostBrowserLauncherParams {
} catch (PackageManager.NameNotFoundException e) {
return null;
}
if (shareActivityInfo == null || shareActivityInfo.metaData == null) {
if (shareActivityInfo == null) {
return null;
}
Bundle metaData = shareActivityInfo.metaData;
return shareActivityInfo.metaData;
}
private static boolean doesShareTargetUsePost(Bundle shareTargetMetaData) {
String method = shareTargetMetaData.getString(WebApkMetaDataKeys.SHARE_METHOD);
if (TextUtils.isEmpty(method)) {
return false;
}
return "POST".equals(method.toUpperCase(Locale.ENGLISH));
}
String shareAction = metaData.getString(WebApkMetaDataKeys.SHARE_ACTION);
/**
* Computes the start URL for the given share intent and share activity.
* @param shareTargetMetaData Meta data for the share target activity selected by the user.
* @param intent Share intent.
*/
protected static String computeStartUrlForShareTarget(
Bundle shareTargetMetaData, Intent intent) {
if (shareTargetMetaData == null) {
return null;
}
if (doesShareTargetUsePost(shareTargetMetaData)) {
return shareTargetMetaData.getString(WebApkMetaDataKeys.SHARE_ACTION);
}
return computeStartUrlForGETShareTarget(shareTargetMetaData, intent);
}
/**
* Computes the start URL for the given share intent and share activity which sends GET HTTP
* requests.
* @param shareTargetMetaData Meta data for the share target activity selected by the user.
* @param intent Share intent.
*/
private static String computeStartUrlForGETShareTarget(
Bundle shareTargetMetaData, Intent intent) {
String shareAction = shareTargetMetaData.getString(WebApkMetaDataKeys.SHARE_ACTION);
if (TextUtils.isEmpty(shareAction)) {
return null;
}
// These can be null, they are checked downstream.
ArrayList<Pair<String, String>> entryList = new ArrayList<>();
entryList.add(new Pair<>(metaData.getString(WebApkMetaDataKeys.SHARE_PARAM_TITLE),
intent.getStringExtra(Intent.EXTRA_SUBJECT)));
entryList.add(new Pair<>(metaData.getString(WebApkMetaDataKeys.SHARE_PARAM_TEXT),
entryList.add(
new Pair<>(shareTargetMetaData.getString(WebApkMetaDataKeys.SHARE_PARAM_TITLE),
intent.getStringExtra(Intent.EXTRA_SUBJECT)));
entryList.add(new Pair<>(shareTargetMetaData.getString(WebApkMetaDataKeys.SHARE_PARAM_TEXT),
intent.getStringExtra(Intent.EXTRA_TEXT)));
return createWebShareTargetUriString(shareAction, entryList);
return createGETWebShareTargetUriString(shareAction, entryList);
}
/**
* Converts the action url and parameters of a webshare target into a URI.
* Converts the action url and parameters of a GET webshare target into a URI.
* Example:
* - action = "https://example.org/includinator/share.html"
* - params
......@@ -116,7 +156,7 @@ public class HostBrowserLauncherParams {
* TODO(ckitagawa): The escaping behavior isn't entirely correct. The exact encoding is still
* being discussed at https://github.com/WICG/web-share-target/issues/59.
*/
protected static String createWebShareTargetUriString(
protected static String createGETWebShareTargetUriString(
String action, ArrayList<Pair<String, String>> entryList) {
Uri.Builder queryBuilder = new Uri.Builder();
for (Pair<String, String> nameValue : entryList) {
......@@ -143,7 +183,8 @@ public class HostBrowserLauncherParams {
private HostBrowserLauncherParams(String hostBrowserPackageName,
int hostBrowserMajorChromiumVersion, boolean dialogShown, Intent originalIntent,
String startUrl, int source, boolean forceNavigation, long launchTimeMs) {
String startUrl, int source, boolean forceNavigation, long launchTimeMs,
String launchingActivityClassName) {
mHostBrowserPackageName = hostBrowserPackageName;
mHostBrowserMajorChromiumVersion = hostBrowserMajorChromiumVersion;
mDialogShown = dialogShown;
......@@ -152,6 +193,7 @@ public class HostBrowserLauncherParams {
mSource = source;
mForceNavigation = forceNavigation;
mLaunchTimeMs = launchTimeMs;
mLaunchingActivityClassName = launchingActivityClassName;
}
/** Returns the chosen host browser. */
......@@ -199,4 +241,9 @@ public class HostBrowserLauncherParams {
public long getLaunchTimeMs() {
return mLaunchTimeMs;
}
/** Returns the class name of the activity which received the intent.*/
public String getLaunchingActivityClassName() {
return mLaunchingActivityClassName;
}
}
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