Index: chrome/android/webapk/shell_apk/src/org/chromium/webapk/shell_apk/WebApkUtils.java |
diff --git a/chrome/android/webapk/shell_apk/src/org/chromium/webapk/shell_apk/WebApkUtils.java b/chrome/android/webapk/shell_apk/src/org/chromium/webapk/shell_apk/WebApkUtils.java |
index 4096cdc427006ea2b62cd292d7c1146df61c3a87..bc8c754d6fd63be19c5028d606cc9fb6465fd055 100644 |
--- a/chrome/android/webapk/shell_apk/src/org/chromium/webapk/shell_apk/WebApkUtils.java |
+++ b/chrome/android/webapk/shell_apk/src/org/chromium/webapk/shell_apk/WebApkUtils.java |
@@ -5,23 +5,89 @@ |
package org.chromium.webapk.shell_apk; |
import android.content.Context; |
+import android.content.Intent; |
+import android.content.SharedPreferences; |
import android.content.pm.ApplicationInfo; |
import android.content.pm.PackageManager; |
import android.content.pm.PackageManager.NameNotFoundException; |
+import android.content.pm.ResolveInfo; |
+import android.graphics.drawable.Drawable; |
+import android.net.Uri; |
+import android.text.TextUtils; |
+import org.chromium.webapk.lib.common.WebApkConstants; |
import org.chromium.webapk.lib.common.WebApkMetaDataKeys; |
+import java.util.ArrayList; |
+import java.util.Arrays; |
+import java.util.Collections; |
+import java.util.Comparator; |
+import java.util.HashSet; |
+import java.util.List; |
+import java.util.Set; |
+ |
/** |
* Contains utility methods for interacting with WebAPKs. |
*/ |
public class WebApkUtils { |
+ public static final String SHARED_PREF_RUNTIME_HOST = "runtime_host"; |
+ |
+ /** |
+ * The package names of the channels of Chrome that support WebAPKs. The most preferred one |
+ * comes first. |
+ */ |
+ private static List<String> sBrowsersSupportingWebApk = new ArrayList<String>( |
+ Arrays.asList("com.google.android.apps.chrome", "com.android.chrome", "com.chrome.beta", |
+ "com.chrome.dev", "com.chrome.canary")); |
+ |
+ /** Stores information about a potential host browser for the WebAPK. */ |
+ public static class BrowserItem { |
+ private String mPackageName; |
+ private CharSequence mApplicationLabel; |
+ private Drawable mIcon; |
+ private boolean mSupportsWebApks; |
+ |
+ public BrowserItem(String packageName, CharSequence applicationLabel, Drawable icon, |
+ boolean supportsWebApks) { |
+ mPackageName = packageName; |
+ mApplicationLabel = applicationLabel; |
+ mIcon = icon; |
+ mSupportsWebApks = supportsWebApks; |
+ } |
+ |
+ /** Returns the package name of a browser. */ |
+ public String getPackageName() { |
+ return mPackageName; |
+ } |
+ |
+ /** Returns the application name of a browser. */ |
+ public CharSequence getApplicationName() { |
+ return mApplicationLabel; |
+ } |
+ |
+ /** Returns a drawable of the browser icon. */ |
+ public Drawable getApplicationIcon() { |
+ return mIcon; |
+ } |
+ |
+ /** Returns whether the browser supports WebAPKs. */ |
+ public boolean supportsWebApks() { |
+ return mSupportsWebApks; |
+ } |
+ } |
/** |
- * Caches the value read from Application Metadata which specifies the host browser's package |
- * name. |
+ * Caches the package name of the host browser. {@link sHostPackage} might refer to a browser |
+ * which has been uninstalled. A notification can keep the WebAPK process alive after the host |
+ * browser has been uninstalled. |
*/ |
private static String sHostPackage; |
+ /** For testing only. */ |
+ public static void resetCachedHostPackageForTesting() { |
+ sHostPackage = null; |
+ } |
+ |
/** |
* Returns a Context for the host browser that was specified when building the WebAPK. |
* @param context A context. |
@@ -31,8 +97,7 @@ public class WebApkUtils { |
try { |
String hostPackage = getHostBrowserPackageName(context); |
return context.getApplicationContext().createPackageContext( |
- hostPackage, |
- Context.CONTEXT_IGNORE_SECURITY | Context.CONTEXT_INCLUDE_CODE); |
+ hostPackage, Context.CONTEXT_IGNORE_SECURITY | Context.CONTEXT_INCLUDE_CODE); |
} catch (NameNotFoundException e) { |
e.printStackTrace(); |
} |
@@ -40,15 +105,19 @@ public class WebApkUtils { |
} |
/** |
- * Returns the package name for the host browser that was specified when building the WebAPK. |
+ * Returns the package name of the host browser to launch the WebAPK. Also caches the package |
+ * name in the SharedPreference if it is not null. |
* @param context A context. |
* @return The package name. Returns null on an error. |
*/ |
public static String getHostBrowserPackageName(Context context) { |
- if (sHostPackage != null) return sHostPackage; |
- String hostPackage = readMetaDataFromManifest(context, WebApkMetaDataKeys.RUNTIME_HOST); |
- // Set {@link sHostPackage} to a non-null value so that the value is computed only once. |
- sHostPackage = hostPackage != null ? hostPackage : ""; |
+ if (sHostPackage == null) { |
+ sHostPackage = getHostBrowserPackageNameInternal(context); |
+ if (sHostPackage != null) { |
+ writeHostBrowserToSharedPref(context, sHostPackage); |
+ } |
+ } |
+ |
return sHostPackage; |
} |
@@ -64,6 +133,84 @@ public class WebApkUtils { |
return ai.metaData.getString(key); |
} |
+ /** |
+ * Returns the package name of the host browser to launch the WebAPK, or null if we did not find |
+ * one. |
+ */ |
+ private static String getHostBrowserPackageNameInternal(Context context) { |
+ Set<String> installedBrowsers = getInstalledBrowsers(context.getPackageManager()); |
+ if (installedBrowsers.isEmpty()) { |
+ return null; |
+ } |
+ |
+ // Gets the package name of the host browser if it is stored in the SharedPreference. |
+ String cachedHostBrowser = getHostBrowserFromSharedPreference(context); |
+ if (!TextUtils.isEmpty(cachedHostBrowser) |
+ && installedBrowsers.contains(cachedHostBrowser)) { |
+ return cachedHostBrowser; |
+ } |
+ |
+ // Gets the package name of the host browser if it is specified in AndroidManifest.xml. |
+ String hostBrowserFromManifest = |
+ readMetaDataFromManifest(context, WebApkMetaDataKeys.RUNTIME_HOST); |
+ if (!TextUtils.isEmpty(hostBrowserFromManifest)) { |
+ if (installedBrowsers.contains(hostBrowserFromManifest)) { |
+ return hostBrowserFromManifest; |
+ } |
+ return null; |
+ } |
+ |
+ // Gets the package name of the default browser on the Android device. |
+ // TODO(hanxi): Investigate the best way to know which browser supports WebAPKs. |
+ String defaultBrowser = getDefaultBrowserPackageName(context.getPackageManager()); |
+ if (!TextUtils.isEmpty(defaultBrowser) && installedBrowsers.contains(defaultBrowser) |
+ && sBrowsersSupportingWebApk.contains(defaultBrowser)) { |
+ return defaultBrowser; |
+ } |
+ |
+ return null; |
+ } |
+ |
+ /** |
+ * Returns a list of browsers to choose host browser from. The list includes all the installed |
+ * browsers, and if none of the installed browser supports WebAPKs, Chrome will be added to the |
+ * list as well. |
+ */ |
+ public static List<BrowserItem> getBrowserInfosForHostBrowserSelection( |
+ PackageManager packageManager) { |
+ Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://")); |
+ List<ResolveInfo> resolvedActivityList = packageManager.queryIntentActivities( |
+ browserIntent, PackageManager.MATCH_DEFAULT_ONLY); |
+ |
+ boolean hasBrowserSupportingWebApks = false; |
+ List<BrowserItem> browsers = new ArrayList<>(); |
+ for (ResolveInfo info : resolvedActivityList) { |
+ boolean supportsWebApk = false; |
+ if (sBrowsersSupportingWebApk.contains(info.activityInfo.packageName)) { |
+ supportsWebApk = true; |
+ hasBrowserSupportingWebApks = true; |
+ } |
+ browsers.add(new BrowserItem(info.activityInfo.packageName, |
+ info.loadLabel(packageManager), info.loadIcon(packageManager), supportsWebApk)); |
+ } |
+ |
+ Collections.sort(browsers, new Comparator<BrowserItem>() { |
+ @Override |
+ public int compare(BrowserItem a, BrowserItem b) { |
+ if (a.mSupportsWebApks == b.mSupportsWebApks) { |
+ return a.getPackageName().compareTo(b.getPackageName()); |
+ } |
+ return a.mSupportsWebApks ? -1 : 1; |
+ } |
+ }); |
+ |
+ if (hasBrowserSupportingWebApks) return browsers; |
+ |
+ // TODO(hanxi): add Chrome's icon to WebAPKs. |
+ browsers.add(new BrowserItem("com.android.chrome", "Chrome", null, true)); |
+ return browsers; |
+ } |
+ |
/** |
* Returns the uid for the host browser that was specified when building the WebAPK. |
* @param context A context. |
@@ -84,4 +231,58 @@ public class WebApkUtils { |
} |
return -1; |
} |
+ |
+ /** Returns the package name of the host browser cached in the SharedPreferences. */ |
+ public static String getHostBrowserFromSharedPreference(Context context) { |
+ SharedPreferences sharedPref = |
+ context.getSharedPreferences(WebApkConstants.PREF_PACKAGE, Context.MODE_PRIVATE); |
+ return sharedPref.getString(SHARED_PREF_RUNTIME_HOST, null); |
+ } |
+ |
+ /** Returns a set of package names of all the installed browsers on the device. */ |
+ public static Set<String> getInstalledBrowsers(PackageManager packageManager) { |
+ Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://")); |
+ List<ResolveInfo> resolvedActivityList = packageManager.queryIntentActivities( |
+ browserIntent, PackageManager.MATCH_DEFAULT_ONLY); |
+ |
+ Set<String> packagesSupportingWebApks = new HashSet<String>(); |
+ for (ResolveInfo info : resolvedActivityList) { |
+ packagesSupportingWebApks.add(info.activityInfo.packageName); |
+ } |
+ return packagesSupportingWebApks; |
+ } |
+ |
+ /** Returns the package name of the default browser on the Android device. */ |
+ private static String getDefaultBrowserPackageName(PackageManager packageManager) { |
+ Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://")); |
+ ResolveInfo resolveInfo = |
+ packageManager.resolveActivity(browserIntent, PackageManager.MATCH_DEFAULT_ONLY); |
+ if (resolveInfo == null || resolveInfo.activityInfo == null) return null; |
+ |
+ return resolveInfo.activityInfo.packageName; |
+ } |
+ |
+ /** |
+ * Writes the package name of the host browser to the SharedPreferences. If the host browser is |
+ * different than the previous one stored, delete the SharedPreference before storing the new |
+ * host browser. |
+ */ |
+ public static void writeHostBrowserToSharedPref(Context context, String hostPackage) { |
+ if (TextUtils.isEmpty(hostPackage)) return; |
+ |
+ SharedPreferences sharedPref = |
+ context.getSharedPreferences(WebApkConstants.PREF_PACKAGE, Context.MODE_PRIVATE); |
+ SharedPreferences.Editor editor = sharedPref.edit(); |
+ editor.putString(SHARED_PREF_RUNTIME_HOST, hostPackage); |
+ editor.apply(); |
+ } |
+ |
+ /** Deletes the SharedPreferences. */ |
+ public static void deleteSharedPref(Context context) { |
+ SharedPreferences sharedPref = |
+ context.getSharedPreferences(WebApkConstants.PREF_PACKAGE, Context.MODE_PRIVATE); |
+ SharedPreferences.Editor editor = sharedPref.edit(); |
+ editor.clear(); |
+ editor.apply(); |
+ } |
} |