blob: 23eb86177532c91c09c56d0fb05c98eb103cb227 [file] [log] [blame]
// Copyright 2015 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.io.File;
import java.util.List;
/**
* WebAPK's main Activity.
*/
public class MainActivity extends Activity {
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_MainActivity";
/**
* Name of class which launches browser in WebAPK mode.
*/
private static final String HOST_BROWSER_LAUNCHER_CLASS_NAME =
"org.chromium.webapk.lib.runtime_library.HostBrowserLauncher";
// Action for launching {@link WebappLauncherActivity}.
// TODO(hanxi): crbug.com/737556. Replaces this string with the new WebAPK launch action after
// it is propagated to all the Chrome's channels.
public static final String ACTION_START_WEBAPK =
"com.google.android.apps.chrome.webapps.WebappManager.ACTION_START_WEBAPP";
// Must stay in sync with
// {@link org.chromium.chrome.browser.ShortcutHelper#REUSE_URL_MATCHING_TAB_ELSE_NEW_TAB}.
private static final String REUSE_URL_MATCHING_TAB_ELSE_NEW_TAB =
"REUSE_URL_MATCHING_TAB_ELSE_NEW_TAB";
/**
* Key for passing app icon id.
*/
private static final String KEY_APP_ICON_ID = "app_icon_id";
/**
* The URL to launch the WebAPK. Used in the case of deep-links (intents from other apps) that
* fall into the WebAPK scope.
*/
private String mOverrideUrl;
/** The "start_url" baked in the AndroidManifest.xml. */
private String mStartUrl;
/**
* 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;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Bundle metadata = WebApkUtils.readMetaData(this);
if (metadata == null) {
finish();
return;
}
mOverrideUrl = getOverrideUrl();
mStartUrl = (mOverrideUrl != null) ? mOverrideUrl
: metadata.getString(WebApkMetaDataKeys.START_URL);
if (mStartUrl == null) {
finish();
return;
}
// The WebAPK may have been launched as a result of an intent filter for a different
// scheme or top level domain. Rewrite the scheme and host name to the scope's
// scheme and host name.
String scopeUrl = metadata.getString(WebApkMetaDataKeys.SCOPE);
if (!TextUtils.isEmpty(scopeUrl)) {
Uri parsedStartUrl = Uri.parse(mStartUrl);
Uri parsedScope = Uri.parse(scopeUrl);
mStartUrl = parsedStartUrl.buildUpon()
.scheme(parsedScope.getScheme())
.encodedAuthority(parsedScope.getEncodedAuthority())
.build()
.toString();
}
Log.v(TAG, "Url of the WebAPK: " + mStartUrl);
String packageName = getPackageName();
Log.v(TAG, "Package name of the WebAPK:" + packageName);
String runtimeHostInPreferences = WebApkUtils.getHostBrowserFromSharedPreference(this);
String runtimeHost = WebApkUtils.getHostBrowserPackageName(this);
if (!TextUtils.isEmpty(runtimeHostInPreferences)
&& !runtimeHostInPreferences.equals(runtimeHost)) {
deleteSharedPref(this);
deleteInternalStorage();
}
if (!TextUtils.isEmpty(runtimeHost)) {
launchInHostBrowser(runtimeHost);
finish();
return;
}
List<ResolveInfo> infos = WebApkUtils.getInstalledBrowserResolveInfos(getPackageManager());
if (hasBrowserSupportingWebApks(infos)) {
showChooseHostBrowserDialog(infos);
} else {
showInstallHostBrowserDialog(metadata);
}
}
/** Deletes the SharedPreferences. */
private void deleteSharedPref(Context context) {
SharedPreferences sharedPref =
context.getSharedPreferences(WebApkConstants.PREF_PACKAGE, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPref.edit();
editor.clear();
editor.apply();
}
/** Deletes the internal storage. */
private void deleteInternalStorage() {
deletePath(getCacheDir());
deletePath(getFilesDir());
deletePath(getDir(HostBrowserClassLoader.DEX_DIR_NAME, Context.MODE_PRIVATE));
}
private void deletePath(File file) {
if (file == null) return;
if (file.isDirectory()) {
File[] children = file.listFiles();
if (children != null) {
for (File child : children) {
deletePath(child);
}
}
}
if (!file.delete()) {
Log.e(TAG, "Failed to delete : " + file.getAbsolutePath());
}
}
private void launchInHostBrowser(String runtimeHost) {
boolean forceNavigation = false;
int source = getIntent().getIntExtra(WebApkConstants.EXTRA_SOURCE, 0);
if (mOverrideUrl != null) {
if (source == WebApkConstants.SHORTCUT_SOURCE_UNKNOWN) {
source = WebApkConstants.SHORTCUT_SOURCE_EXTERNAL_INTENT;
}
forceNavigation =
getIntent().getBooleanExtra(WebApkConstants.EXTRA_FORCE_NAVIGATION, true);
}
// 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.
Intent intent = new Intent();
intent.setAction(ACTION_START_WEBAPK);
intent.setPackage(runtimeHost);
intent.putExtra(WebApkConstants.EXTRA_URL, mStartUrl)
.putExtra(WebApkConstants.EXTRA_SOURCE, source)
.putExtra(WebApkConstants.EXTRA_WEBAPK_PACKAGE_NAME, getPackageName())
.putExtra(WebApkConstants.EXTRA_FORCE_NAVIGATION, forceNavigation);
try {
startActivity(intent);
} catch (ActivityNotFoundException e) {
Log.w(TAG, "Unable to launch browser in WebAPK mode.");
e.printStackTrace();
}
}
/**
* Launches the Play Store with the host browser's page.
*/
private void installBrowser(String hostBrowserPackageName) {
try {
startActivity(createInstallIntent(hostBrowserPackageName));
} catch (ActivityNotFoundException e) {
}
}
/** 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;
}
/** Returns whether there is any installed browser supporting WebAPKs. */
private static boolean hasBrowserSupportingWebApks(List<ResolveInfo> resolveInfos) {
List<String> browsersSupportingWebApk = WebApkUtils.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) {
ChooseHostBrowserDialog.DialogListener listener =
new ChooseHostBrowserDialog.DialogListener() {
@Override
public void onHostBrowserSelected(String selectedHostBrowser) {
launchInHostBrowser(selectedHostBrowser);
WebApkUtils.writeHostBrowserToSharedPref(
MainActivity.this, selectedHostBrowser);
finish();
}
@Override
public void onQuit() {
finish();
}
};
ChooseHostBrowserDialog.show(this, listener, infos, getString(R.string.name));
}
/** Shows a dialog to install the host browser. */
private void showInstallHostBrowserDialog(Bundle metadata) {
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);
WebApkUtils.writeHostBrowserToSharedPref(MainActivity.this, packageName);
finish();
}
@Override
public void onConfirmQuit() {
finish();
}
};
InstallHostBrowserDialog.show(this, listener, getString(R.string.name),
lastResortHostBrowserPackageName, lastResortHostBrowserApplicationName,
R.drawable.last_resort_runtime_host_logo);
}
}