Commit 25f681ee authored by Peter Kotwicz's avatar Peter Kotwicz Committed by Commit Bot

[Android WebAPK Refactor] Minimize code in MainActivity.java 5/X

This CL:
- Makes code in MainActivity.java more modular.
- Splits HostBrowserLauncher into:
  - a part for selecting the host browser
  - a part for launching the host browser

This CL is in preparation for:
- Introducing a version of MainActivity.java for NewSplashWebApk.apk
- Implementing the logic to determine whether to show the splash screen
inside of the WebAPK or inside of Chrome. The host browser must be known
in order to make this decision.

BUG=817263

Change-Id: I024e390c57b4da09fe0e4e2fcbaf616c6bad2e8b
Reviewed-on: https://chromium-review.googlesource.com/1220054
Commit-Queue: Peter Kotwicz <pkotwicz@chromium.org>
Reviewed-by: default avatarXi Han <hanxi@chromium.org>
Cr-Commit-Position: refs/heads/master@{#593421}
parent 60e3f8ec
......@@ -35,11 +35,14 @@ template("webapk_java") {
java_files = [
"src/org/chromium/webapk/shell_apk/HostBrowserClassLoader.java",
"src/org/chromium/webapk/shell_apk/HostBrowserLauncher.java",
"src/org/chromium/webapk/shell_apk/HostBrowserLauncherParams.java",
"src/org/chromium/webapk/shell_apk/HostBrowserLauncherActivity.java",
"src/org/chromium/webapk/shell_apk/ChooseHostBrowserDialog.java",
"src/org/chromium/webapk/shell_apk/HostBrowserUtils.java",
"src/org/chromium/webapk/shell_apk/IdentityService.java",
"src/org/chromium/webapk/shell_apk/InstallHostBrowserDialog.java",
"src/org/chromium/webapk/shell_apk/MainActivity.java",
"src/org/chromium/webapk/shell_apk/LaunchHostBrowserSelector.java",
"src/org/chromium/webapk/shell_apk/ShareActivity.java",
"src/org/chromium/webapk/shell_apk/WebApkServiceFactory.java",
"src/org/chromium/webapk/shell_apk/WebApkServiceImplWrapper.java",
......@@ -231,9 +234,9 @@ android_library("shell_apk_javatests") {
junit_binary("webapk_shell_apk_junit_tests") {
java_files = [
"junit/src/org/chromium/webapk/shell_apk/HostBrowserClassLoaderTest.java",
"junit/src/org/chromium/webapk/shell_apk/HostBrowserLauncherParamsTest.java",
"junit/src/org/chromium/webapk/shell_apk/HostBrowserUtilsTest.java",
"junit/src/org/chromium/webapk/shell_apk/MainActivityTest.java",
"junit/src/org/chromium/webapk/shell_apk/ShareActivityTest.java",
"junit/src/org/chromium/webapk/shell_apk/WebApkServiceImplWrapperTest.java",
"junit/src/org/chromium/webapk/shell_apk/WebApkUtilsTest.java",
]
......
......@@ -16,14 +16,14 @@ import org.chromium.testing.local.LocalRobolectricTestRunner;
import java.util.ArrayList;
/**
* Tests for ShareActivity's WebShareTarget parsing.
* Tests for HostBrowserLauncherParams's WebShareTarget parsing.
*/
@RunWith(LocalRobolectricTestRunner.class)
@Config(manifest = Config.NONE)
public class ShareActivityTest {
public class HostBrowserLauncherParamsTest {
/*
* Test that {@link ShareActivity#createWebShareTargetUriString()} handles adding query
* parameters to an action url with different path formats.
* Test that {@link HostBrowserLauncherParams#createWebShareTargetUriString()} handles adding
* query parameters to an action url with different path formats.
*/
@Test
public void testCreateWebShareTargetUriStringBasic() {
......@@ -31,24 +31,26 @@ public class ShareActivityTest {
params.add(new Pair<>("title", "mytitle"));
params.add(new Pair<>("foo", "bar"));
String uri =
ShareActivity.createWebShareTargetUriString("https://www.chromium.org/wst", params);
String uri = HostBrowserLauncherParams.createWebShareTargetUriString(
"https://www.chromium.org/wst", params);
Assert.assertEquals("https://www.chromium.org/wst?title=mytitle&foo=bar", uri);
uri = ShareActivity.createWebShareTargetUriString("https://www.chromium.org/wst/", params);
uri = HostBrowserLauncherParams.createWebShareTargetUriString(
"https://www.chromium.org/wst/", params);
Assert.assertEquals("https://www.chromium.org/wst/?title=mytitle&foo=bar", uri);
uri = ShareActivity.createWebShareTargetUriString(
uri = HostBrowserLauncherParams.createWebShareTargetUriString(
"https://www.chromium.org/base/wst.html", params);
Assert.assertEquals("https://www.chromium.org/base/wst.html?title=mytitle&foo=bar", uri);
uri = ShareActivity.createWebShareTargetUriString(
uri = HostBrowserLauncherParams.createWebShareTargetUriString(
"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 ShareActivity#createWebShareTargetUriString()} skips null names or values.
* Test that {@link HostBrowserLauncherParams#createWebShareTargetUriString()} skips null names
* or values.
*/
@Test
public void testCreateWebShareTargetUriStringNull() {
......@@ -58,28 +60,29 @@ public class ShareActivityTest {
params.add(new Pair<>("hello", "world"));
params.add(new Pair<>(null, null));
// The baseUrl, shareAction and params are checked to be non-null in
// ShareActivity#extractShareTarget.
String uri =
ShareActivity.createWebShareTargetUriString("https://www.chromium.org/wst", params);
// HostBrowserLauncherParams#extractShareTarget.
String uri = HostBrowserLauncherParams.createWebShareTargetUriString(
"https://www.chromium.org/wst", params);
Assert.assertEquals("https://www.chromium.org/wst?hello=world", uri);
}
/*
* Test that {@link ShareActivity#createWebShareTargetUriString()} handles replacing the query
* string of an action url with an existing query.
* Test that {@link HostBrowserLauncherParams#createWebShareTargetUriString()} 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 = ShareActivity.createWebShareTargetUriString(
String uri = HostBrowserLauncherParams.createWebShareTargetUriString(
"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 ShareActivity#createWebShareTargetUriString()} escapes characters.
* Test that {@link HostBrowserLauncherParams#createWebShareTargetUriString()} escapes
* characters.
*/
@Test
public void testCreateWebShareTargetEscaping() {
......@@ -87,7 +90,7 @@ public class ShareActivityTest {
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 = ShareActivity.createWebShareTargetUriString(
String uri = HostBrowserLauncherParams.createWebShareTargetUriString(
"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",
......
......@@ -85,33 +85,6 @@ public class WebApkUtilsTest {
WebApkUtils.rewriteIntentUrlIfNecessary(intentStartUrl, bundle));
}
/**
* Tests that a WebAPK should be launched as a tab if Chrome's version number is lower than
* {@link WebApkUtils#MINIMUM_REQUIRED_CHROME_VERSION}.
*/
@Test
public void testShouldLaunchInTabWhenChromeVersionIsTooLow() {
String versionName = "56.0.0000.0";
Assert.assertTrue(WebApkUtils.shouldLaunchInTab(versionName));
}
/**
* Tests that a WebAPK should not be launched as a tab if Chrome's version is higher or equal to
* {@link WebApkUtils#MINIMUM_REQUIRED_CHROME_VERSION}.
*/
@Test
public void testShouldNotLaunchInTabWithNewVersionOfChrome() {
String versionName = "57.0.0000.0";
Assert.assertFalse(WebApkUtils.shouldLaunchInTab(versionName));
}
/** Tests that a WebAPK should not be launched as a tab in a developer build of Chrome. */
@Test
public void testShouldNotLaunchInTabWithDevBuild() {
String versionName = "Developer Build";
Assert.assertFalse(WebApkUtils.shouldLaunchInTab(versionName));
}
/**
* Tests that {@link WebApkUtils#isInstalled} returns false for an installed but disabled app.
*/
......
......@@ -6,7 +6,7 @@
# (including AndroidManifest.xml) is updated. This version should be incremented
# prior to uploading a new ShellAPK to the WebAPK Minting Server.
# Does not affect Chrome.apk
template_shell_apk_version = 53
template_shell_apk_version = 54
# The ShellAPK version expected by Chrome. Chrome will try to update the WebAPK
# if the WebAPK's ShellAPK version is less than |expected_shell_apk_version|.
......
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.webapk.shell_apk;
import android.app.Activity;
import android.os.Bundle;
import android.os.SystemClock;
/**
* UI-less activity which launches host browser.
*/
public class HostBrowserLauncherActivity extends Activity {
private long mActivityStartTimeMs;
@Override
protected void onCreate(Bundle savedInstanceState) {
mActivityStartTimeMs = SystemClock.elapsedRealtime();
super.onCreate(savedInstanceState);
new LaunchHostBrowserSelector(this).selectHostBrowser(
new LaunchHostBrowserSelector.Callback() {
@Override
public void onBrowserSelected(
String hostBrowserPackageName, boolean dialogShown) {
launchHostBrowser(hostBrowserPackageName, dialogShown);
finish();
}
});
}
private void launchHostBrowser(String hostBrowserPackageName, boolean dialogShown) {
if (hostBrowserPackageName == null) return;
HostBrowserLauncherParams params =
HostBrowserLauncherParams.createForIntent(this, getComponentName(), getIntent(),
hostBrowserPackageName, dialogShown, mActivityStartTimeMs);
if (params == null) return;
HostBrowserLauncher.launch(this, params);
}
}
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.webapk.shell_apk;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Pair;
import org.chromium.webapk.lib.common.WebApkConstants;
import org.chromium.webapk.lib.common.WebApkMetaDataKeys;
import java.util.ArrayList;
/** Convenience wrapper for parameters to {@link HostBrowserLauncher} methods. */
public class HostBrowserLauncherParams {
private String mHostBrowserPackageName;
private int mHostBrowserMajorChromiumVersion;
private boolean mDialogShown;
private Intent mOriginalIntent;
private String mStartUrl;
private int mSource;
private boolean mForceNavigation;
private long mLaunchTimeMs;
/**
* Constructs a HostBrowserLauncherParams object from the passed in Intent and from <meta-data>
* in the Android Manifest.
*/
public static HostBrowserLauncherParams createForIntent(Context context,
ComponentName callingActivityComponentName, Intent intent,
String hostBrowserPackageName, boolean dialogShown, long launchTimeMs) {
Bundle metadata = WebApkUtils.readMetaData(context);
if (metadata == null) return null;
int hostBrowserMajorChromiumVersion = HostBrowserUtils.queryHostBrowserMajorChromiumVersion(
context, hostBrowserPackageName);
String startUrl = null;
int source = WebApkConstants.SHORTCUT_SOURCE_UNKNOWN;
boolean forceNavigation = false;
if (Intent.ACTION_SEND.equals(intent.getAction())) {
startUrl = computeStartUrlForShareTarget(context, callingActivityComponentName, intent);
source = WebApkConstants.SHORTCUT_SOURCE_SHARE;
forceNavigation = true;
} else if (doesUrlUseHttpOrHttpsScheme(intent.getDataString())) {
startUrl = intent.getDataString();
source = intent.getIntExtra(
WebApkConstants.EXTRA_SOURCE, WebApkConstants.SHORTCUT_SOURCE_EXTERNAL_INTENT);
forceNavigation = intent.getBooleanExtra(WebApkConstants.EXTRA_FORCE_NAVIGATION, true);
} else {
startUrl = metadata.getString(WebApkMetaDataKeys.START_URL);
source = WebApkConstants.SHORTCUT_SOURCE_UNKNOWN;
forceNavigation = false;
}
if (startUrl == null) return null;
startUrl = WebApkUtils.rewriteIntentUrlIfNecessary(startUrl, metadata);
return new HostBrowserLauncherParams(hostBrowserPackageName,
hostBrowserMajorChromiumVersion, dialogShown, intent, startUrl, source,
forceNavigation, launchTimeMs);
}
/** Computes the start URL for the given share intent and share activity. */
private static String computeStartUrlForShareTarget(
Context context, ComponentName shareTargetComponentName, Intent intent) {
ActivityInfo shareActivityInfo;
try {
shareActivityInfo = context.getPackageManager().getActivityInfo(
shareTargetComponentName, PackageManager.GET_META_DATA);
} catch (PackageManager.NameNotFoundException e) {
return null;
}
if (shareActivityInfo == null || shareActivityInfo.metaData == null) {
return null;
}
Bundle metaData = shareActivityInfo.metaData;
String shareAction = metaData.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),
intent.getStringExtra(Intent.EXTRA_TEXT)));
return createWebShareTargetUriString(shareAction, entryList);
}
/**
* Converts the action url and parameters of a webshare target into a URI.
* Example:
* - action = "https://example.org/includinator/share.html"
* - params
* title param: "title"
* title intent: "news"
* text param: "description"
* text intent: "story"
* Becomes:
* https://example.org/includinator/share.html?title=news&description=story
* 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(
String action, ArrayList<Pair<String, String>> entryList) {
Uri.Builder queryBuilder = new Uri.Builder();
for (Pair<String, String> nameValue : entryList) {
if (!TextUtils.isEmpty(nameValue.first) && !TextUtils.isEmpty(nameValue.second)) {
// Uri.Builder does URL escaping.
queryBuilder.appendQueryParameter(nameValue.first, nameValue.second);
}
}
Uri shareUri = Uri.parse(action);
Uri.Builder builder = shareUri.buildUpon();
// Uri.Builder uses %20 rather than + for spaces, the spec requires +.
builder.encodedQuery(queryBuilder.build().toString().replace("%20", "+").substring(1));
return builder.build().toString();
}
/** Returns whether the URL uses the HTTP or HTTPs schemes. */
private static boolean doesUrlUseHttpOrHttpsScheme(String url) {
return url != null && (url.startsWith("http:") || url.startsWith("https:"));
}
private HostBrowserLauncherParams(String hostBrowserPackageName,
int hostBrowserMajorChromiumVersion, boolean dialogShown, Intent originalIntent,
String startUrl, int source, boolean forceNavigation, long launchTimeMs) {
mHostBrowserPackageName = hostBrowserPackageName;
mHostBrowserMajorChromiumVersion = hostBrowserMajorChromiumVersion;
mDialogShown = dialogShown;
mOriginalIntent = originalIntent;
mStartUrl = startUrl;
mSource = source;
mForceNavigation = forceNavigation;
mLaunchTimeMs = launchTimeMs;
}
/** Returns the chosen host browser. */
public String hostBrowserPackageName() {
return mHostBrowserPackageName;
}
/**
* Returns the major version of the host browser. Currently, only Chromium host browsers
* (Chrome Canary, Chrome Dev ...) are supported.
*/
public int hostBrowserMajorChromiumVersion() {
return mHostBrowserMajorChromiumVersion;
}
/** Returns whether the choose-host-browser dialog was shown. */
public boolean dialogShown() {
return mDialogShown;
}
/** Returns intent used to launch WebAPK. */
public Intent originalIntent() {
return mOriginalIntent;
}
/** Returns URL to launch WebAPK at. */
public String startUrl() {
return mStartUrl;
}
/** Returns the source which is launching/navigating the WebAPK. */
public int source() {
return mSource;
}
/**
* Returns whether the WebAPK should be navigated to {@link mStartUrl} if it is already
* running.
*/
public boolean forceNavigation() {
return mForceNavigation;
}
/** Returns time in milliseconds that the WebAPK was launched. */
public long launchTimeMs() {
return mLaunchTimeMs;
}
}
......@@ -8,6 +8,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
......@@ -28,6 +29,11 @@ import java.util.Set;
public class HostBrowserUtils {
public static final String SHARED_PREF_RUNTIME_HOST = "runtime_host";
private static final int MINIMUM_REQUIRED_CHROME_VERSION = 57;
/** Version name for developer builds. */
private static final String DEVELOPER_BUILD_VERSION_NAME = "Developer Build";
private static final String TAG = "cr_HostBrowserUtils";
/**
......@@ -203,4 +209,33 @@ public class HostBrowserUtils {
editor.putString(SHARED_PREF_RUNTIME_HOST, hostPackage);
editor.apply();
}
/** Queries the given host browser's major version. */
public static int queryHostBrowserMajorChromiumVersion(
Context context, String hostBrowserPackageName) {
PackageInfo info;
try {
info = context.getPackageManager().getPackageInfo(hostBrowserPackageName, 0);
} catch (PackageManager.NameNotFoundException e) {
return -1;
}
String versionName = info.versionName;
int dotIndex = versionName.indexOf(".");
if (dotIndex < 0) {
if (DEVELOPER_BUILD_VERSION_NAME.equals(versionName)) {
return MINIMUM_REQUIRED_CHROME_VERSION;
}
return -1;
}
try {
return Integer.parseInt(versionName.substring(0, dotIndex));
} catch (NumberFormatException e) {
return -1;
}
}
/** Returns whether a WebAPK should be launched as a tab. See crbug.com/772398. */
public static boolean shouldLaunchInTab(int hostBrowserChromiumMajorVersion) {
return hostBrowserChromiumMajorVersion < MINIMUM_REQUIRED_CHROME_VERSION;
}
}
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.webapk.shell_apk;
import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
import org.chromium.webapk.lib.common.WebApkConstants;
import org.chromium.webapk.lib.common.WebApkMetaDataKeys;
import java.util.List;
/** Selects host browser to launch, showing a dialog to select browser if necessary. */
public class LaunchHostBrowserSelector {
private static final String LAST_RESORT_HOST_BROWSER = "com.android.chrome";
private static final String LAST_RESORT_HOST_BROWSER_APPLICATION_NAME = "Google Chrome";
private static final String TAG = "cr_LaunchHostBrowserSelector";
private Context mContext;
/** Parent activity for any dialogs. */
private Activity mParentActivity;
/**
* Called once {@link #selectHostBrowser()} has selected the host browser either
* via a shared preferences/<meta-data> lookup or via the user selecting the host
* browser from a dialog.
*/
public static interface Callback {
void onBrowserSelected(String hostBrowserPackageName, boolean dialogShown);
}
public LaunchHostBrowserSelector(Activity parentActivity) {
mParentActivity = parentActivity;
mContext = parentActivity.getApplicationContext();
}
/**
* Creates install Intent.
* @param packageName Package to install.
* @return The intent.
*/
private static Intent createInstallIntent(String packageName) {
String marketUrl = "market://details?id=" + packageName;
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(marketUrl));
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
return intent;
}
/**
* Selects host browser to launch, showing a dialog to select browser if necessary. Calls
* {@link selectCallback} with the result.
*/
public void selectHostBrowser(Callback selectCallback) {
Bundle metadata = WebApkUtils.readMetaData(mContext);
if (metadata == null) {
selectCallback.onBrowserSelected(null, false /* dialogShown */);
return;
}
String packageName = mContext.getPackageName();
Log.v(TAG, "Package name of the WebAPK:" + packageName);
String runtimeHostInPreferences =
HostBrowserUtils.getHostBrowserFromSharedPreference(mContext);
String runtimeHost = HostBrowserUtils.getHostBrowserPackageName(mContext);
if (!TextUtils.isEmpty(runtimeHostInPreferences)
&& !runtimeHostInPreferences.equals(runtimeHost)) {
deleteSharedPref();
deleteInternalStorage();
}
if (!TextUtils.isEmpty(runtimeHost)) {
selectCallback.onBrowserSelected(runtimeHost, false /* dialogShown */);
return;
}
List<ResolveInfo> infos =
WebApkUtils.getInstalledBrowserResolveInfos(mContext.getPackageManager());
if (hasBrowserSupportingWebApks(infos)) {
showChooseHostBrowserDialog(infos, selectCallback);
} else {
showInstallHostBrowserDialog(metadata, selectCallback);
}
}
/** Deletes the SharedPreferences. */
private void deleteSharedPref() {
SharedPreferences sharedPref =
mContext.getSharedPreferences(WebApkConstants.PREF_PACKAGE, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPref.edit();
editor.clear();
editor.apply();
}
/** Deletes the internal storage. */
private void deleteInternalStorage() {
DexLoader.deletePath(mContext.getCacheDir());
DexLoader.deletePath(mContext.getFilesDir());
DexLoader.deletePath(
mContext.getDir(HostBrowserClassLoader.DEX_DIR_NAME, Context.MODE_PRIVATE));
}
/**
* Launches the Play Store with the host browser's page.
*/
private void installBrowser(String hostBrowserPackageName) {
try {
mParentActivity.startActivity(createInstallIntent(hostBrowserPackageName));
} catch (ActivityNotFoundException e) {
}
}
/** Returns whether there is any installed browser supporting WebAPKs. */
private static boolean hasBrowserSupportingWebApks(List<ResolveInfo> resolveInfos) {
List<String> browsersSupportingWebApk = HostBrowserUtils.getBrowsersSupportingWebApk();
for (ResolveInfo info : resolveInfos) {
if (browsersSupportingWebApk.contains(info.activityInfo.packageName)) {
return true;
}
}
return false;
}
/** Shows a dialog to choose the host browser. */
private void showChooseHostBrowserDialog(List<ResolveInfo> infos, Callback selectCallback) {
ChooseHostBrowserDialog.DialogListener listener =
new ChooseHostBrowserDialog.DialogListener() {
@Override
public void onHostBrowserSelected(String selectedHostBrowser) {
HostBrowserUtils.writeHostBrowserToSharedPref(
mContext, selectedHostBrowser);
selectCallback.onBrowserSelected(
selectedHostBrowser, true /* dialogShown */);
}
@Override
public void onQuit() {
selectCallback.onBrowserSelected(null, true /* dialogShown */);
}
};
ChooseHostBrowserDialog.show(
mParentActivity, listener, infos, mContext.getString(R.string.name));
}
/** Shows a dialog to install the host browser. */
private void showInstallHostBrowserDialog(Bundle metadata, Callback selectCallback) {
String lastResortHostBrowserPackageName =
metadata.getString(WebApkMetaDataKeys.RUNTIME_HOST);
String lastResortHostBrowserApplicationName =
metadata.getString(WebApkMetaDataKeys.RUNTIME_HOST_APPLICATION_NAME);
if (TextUtils.isEmpty(lastResortHostBrowserPackageName)) {
// WebAPKs without runtime host specified in the AndroidManifest.xml always install
// Google Chrome as the default host browser.
lastResortHostBrowserPackageName = LAST_RESORT_HOST_BROWSER;
lastResortHostBrowserApplicationName = LAST_RESORT_HOST_BROWSER_APPLICATION_NAME;
}
InstallHostBrowserDialog.DialogListener listener =
new InstallHostBrowserDialog.DialogListener() {
@Override
public void onConfirmInstall(String packageName) {
installBrowser(packageName);
HostBrowserUtils.writeHostBrowserToSharedPref(mContext, packageName);
selectCallback.onBrowserSelected(null, true /* dialogShown */);
}
@Override
public void onConfirmQuit() {
selectCallback.onBrowserSelected(null, true /* dialogShown */);
}
};
InstallHostBrowserDialog.show(mParentActivity, listener, mContext.getString(R.string.name),
lastResortHostBrowserPackageName, lastResortHostBrowserApplicationName,
R.drawable.last_resort_runtime_host_logo);
}
}
......@@ -4,59 +4,7 @@
package org.chromium.webapk.shell_apk;
import android.app.Activity;
import android.os.Bundle;
import android.os.SystemClock;
import org.chromium.webapk.lib.common.WebApkConstants;
import org.chromium.webapk.lib.common.WebApkMetaDataKeys;
/**
* WebAPK's main Activity.
*/
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
long activityStartTime = SystemClock.elapsedRealtime();
super.onCreate(savedInstanceState);
Bundle metadata = WebApkUtils.readMetaData(this);
if (metadata == null) {
finish();
return;
}
String overrideUrl = getOverrideUrl();
String startUrl = (overrideUrl != null) ? overrideUrl
: metadata.getString(WebApkMetaDataKeys.START_URL);
if (startUrl == null) {
finish();
return;
}
startUrl = WebApkUtils.rewriteIntentUrlIfNecessary(startUrl, metadata);
boolean overrideSpecified = (overrideUrl != null);
int source = getIntent().getIntExtra(WebApkConstants.EXTRA_SOURCE,
overrideSpecified ? WebApkConstants.SHORTCUT_SOURCE_EXTERNAL_INTENT
: WebApkConstants.SHORTCUT_SOURCE_UNKNOWN);
// The override URL is non null when the WebAPK is launched from a deep link. The WebAPK
// should navigate to the URL in the deep link even if the WebAPK is already open.
boolean forceNavigation = getIntent().getBooleanExtra(
WebApkConstants.EXTRA_FORCE_NAVIGATION, overrideSpecified);
HostBrowserLauncher launcher = new HostBrowserLauncher(
this, getIntent(), startUrl, source, forceNavigation, activityStartTime);
launcher.selectHostBrowserAndLaunch(() -> finish());
}
/** Retrieves URL from the intent's data. Returns null if a URL could not be retrieved. */
private String getOverrideUrl() {
String overrideUrl = getIntent().getDataString();
if (overrideUrl != null
&& (overrideUrl.startsWith("https:") || overrideUrl.startsWith("http:"))) {
return overrideUrl;
}
return null;
}
}
public class MainActivity extends HostBrowserLauncherActivity {}
......@@ -4,97 +4,7 @@
package org.chromium.webapk.shell_apk;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.SystemClock;
import android.text.TextUtils;
import android.util.Pair;
import org.chromium.webapk.lib.common.WebApkConstants;
import org.chromium.webapk.lib.common.WebApkMetaDataKeys;
import java.util.ArrayList;
/**
* WebAPK's share handler Activity.
*/
public class ShareActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
long activityStartTime = SystemClock.elapsedRealtime();
super.onCreate(savedInstanceState);
String startUrl = extractShareTarget();
if (TextUtils.isEmpty(startUrl)) {
finish();
return;
}
HostBrowserLauncher launcher = new HostBrowserLauncher(this, getIntent(), startUrl,
WebApkConstants.SHORTCUT_SOURCE_SHARE, true /* forceNavigation */,
activityStartTime);
launcher.selectHostBrowserAndLaunch(() -> finish());
}
private String extractShareTarget() {
ActivityInfo shareActivityInfo;
try {
shareActivityInfo = getPackageManager().getActivityInfo(
getComponentName(), PackageManager.GET_META_DATA);
} catch (PackageManager.NameNotFoundException e) {
return null;
}
if (shareActivityInfo == null || shareActivityInfo.metaData == null) {
return null;
}
Bundle metaData = shareActivityInfo.metaData;
String shareAction = metaData.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),
getIntent().getStringExtra(Intent.EXTRA_SUBJECT)));
entryList.add(new Pair<>(metaData.getString(WebApkMetaDataKeys.SHARE_PARAM_TEXT),
getIntent().getStringExtra(Intent.EXTRA_TEXT)));
return createWebShareTargetUriString(shareAction, entryList);
}
/**
* Converts the action url and parameters of a webshare target into a URI.
* Example:
* - action = "https://example.org/includinator/share.html"
* - params
* title param: "title"
* title intent: "news"
* text param: "description"
* text intent: "story"
* Becomes:
* https://example.org/includinator/share.html?title=news&description=story
* 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(
String action, ArrayList<Pair<String, String>> entryList) {
Uri.Builder queryBuilder = new Uri.Builder();
for (Pair<String, String> nameValue : entryList) {
if (!TextUtils.isEmpty(nameValue.first) && !TextUtils.isEmpty(nameValue.second)) {
// Uri.Builder does URL escaping.
queryBuilder.appendQueryParameter(nameValue.first, nameValue.second);
}
}
Uri shareUri = Uri.parse(action);
Uri.Builder builder = shareUri.buildUpon();
// Uri.Builder uses %20 rather than + for spaces, the spec requires +.
builder.encodedQuery(queryBuilder.build().toString().replace("%20", "+").substring(1));
return builder.build().toString();
}
}
public class ShareActivity extends HostBrowserLauncherActivity {}
......@@ -33,7 +33,6 @@ import java.util.List;
* Contains utility methods for interacting with WebAPKs.
*/
public class WebApkUtils {
private static final int MINIMUM_REQUIRED_CHROME_VERSION = 57;
private static final String TAG = "cr_WebApkUtils";
/** Percentage to darken a color by when setting the status bar color. */
......@@ -152,15 +151,6 @@ public class WebApkUtils {
}
}
/** Returns whether a WebAPK should be launched as a tab. See crbug.com/772398. */
public static boolean shouldLaunchInTab(String versionName) {
int dotIndex = versionName.indexOf(".");
if (dotIndex == -1) return false;
int version = Integer.parseInt(versionName.substring(0, dotIndex));
return version < MINIMUM_REQUIRED_CHROME_VERSION;
}
/**
* Calculates the contrast between the given color and white, using the algorithm provided by
* the WCAG v1 in http://www.w3.org/TR/WCAG20/#contrast-ratiodef.
......
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