Index: chrome/android/java/src/org/chromium/chrome/browser/webapps/ManifestUpgradeDetector.java |
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/ManifestUpgradeDetector.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/ManifestUpgradeDetector.java |
index 58708fd6fdcdff4ee538f08c0d1e849cb41cc057..11e529a464f9701bb9d477688d9e9cb237e644de 100644 |
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/ManifestUpgradeDetector.java |
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/ManifestUpgradeDetector.java |
@@ -6,18 +6,22 @@ package org.chromium.chrome.browser.webapps; |
import android.content.pm.ApplicationInfo; |
import android.content.pm.PackageManager; |
+import android.graphics.Bitmap; |
import android.net.Uri; |
+import android.os.Bundle; |
import android.text.TextUtils; |
import org.chromium.base.ContextUtils; |
import org.chromium.base.Log; |
+import org.chromium.chrome.browser.ShortcutHelper; |
import org.chromium.chrome.browser.tab.Tab; |
/** |
* This class checks whether the WebAPK needs to be re-installed and sends a request to re-install |
* the WebAPK if it needs to be re-installed. |
*/ |
-public class ManifestUpgradeDetector implements ManifestUpgradeDetectorFetcher.Callback { |
+public class ManifestUpgradeDetector implements ManifestUpgradeDetectorFetcher.Callback, |
+ ShortcutHelper.FetchHomescreenImageCallback { |
/** |
* The names of <meta-data> in the WebAPK's AndroidManifest.xml whose values are needed to |
* determine whether a WebAPK needs to be upgraded but which are not present in |
@@ -25,20 +29,70 @@ public class ManifestUpgradeDetector implements ManifestUpgradeDetectorFetcher.C |
* {@linkorg.chromium.webapk.lib.runtime_library.HostBrowserLauncher}. |
*/ |
public static final String META_DATA_START_URL = "org.chromium.webapk.shell_apk.startUrl"; |
+ public static final String META_DATA_ICON_URL = "org.chromium.webapk.shell_apk.iconUrl"; |
+ public static final String META_DATA_ICON_MURMUR2_HASH = |
+ "org.chromium.webapk.shell_apk.iconMurmur2Hash"; |
+ |
+ private static final String TAG = "cr_UpgradeDetector"; |
+ |
+ /** |
+ * Fetched Web Manifest data. |
+ */ |
+ private static class FetchedManifestData { |
+ public String startUrl; |
+ public String scopeUrl; |
+ public String name; |
+ public String shortName; |
+ public String iconUrl; |
+ |
+ // Hash of untransformed icon bytes. The hash should have been taken prior to any |
+ // encoding/decoding. |
+ public long iconMurmur2Hash; |
+ |
+ public Bitmap icon; |
+ public int displayMode; |
+ public int orientation; |
+ public long themeColor; |
+ public long backgroundColor; |
+ } |
/** The WebAPK's tab. */ |
private final Tab mTab; |
+ /** |
+ * Web Manifest data at time that the WebAPK was generated. |
+ */ |
private WebappInfo mWebappInfo; |
- |
private String mStartUrl; |
+ private String mIconUrl; |
+ private long mIconMurmur2Hash; |
/** |
* Fetches the WebAPK's Web Manifest from the web. |
*/ |
private ManifestUpgradeDetectorFetcher mFetcher; |
- private static final String TAG = "cr_UpgradeDetector"; |
+ /** |
+ * Fetched Web Manifest data. |
+ */ |
+ private FetchedManifestData mFetchedData; |
+ |
+ /** |
+ * Gets the long value from a Bundle. The long should be terminated with 'L'. This function is |
+ * more reliable than {@link Bundle#getLong()} which returns 0 if the value is below |
+ * Float.MAX_VALUE. Returns 0 if the value could not be parsed. |
+ */ |
+ private static long getLongFromBundle(Bundle bundle, String key) { |
+ String value = bundle.getString(key); |
+ if (value == null || !value.endsWith("L")) { |
+ return 0; |
+ } |
+ try { |
+ return Long.parseLong(value.substring(0, value.length() - 1)); |
+ } catch (NumberFormatException e) { |
+ } |
+ return 0; |
+ } |
public ManifestUpgradeDetector(Tab tab, WebappInfo info) { |
mTab = tab; |
@@ -72,10 +126,13 @@ public class ManifestUpgradeDetector implements ManifestUpgradeDetectorFetcher.C |
private void getMetaDataFromAndroidManifest() { |
try { |
- ApplicationInfo appinfo = |
+ ApplicationInfo appInfo = |
ContextUtils.getApplicationContext().getPackageManager().getApplicationInfo( |
mWebappInfo.webApkPackageName(), PackageManager.GET_META_DATA); |
- mStartUrl = appinfo.metaData.getString(META_DATA_START_URL); |
+ Bundle metaData = appInfo.metaData; |
+ mStartUrl = metaData.getString(META_DATA_START_URL); |
+ mIconUrl = metaData.getString(META_DATA_ICON_URL); |
+ mIconMurmur2Hash = getLongFromBundle(metaData, META_DATA_ICON_MURMUR2_HASH); |
} catch (PackageManager.NameNotFoundException e) { |
e.printStackTrace(); |
} |
@@ -96,17 +153,61 @@ public class ManifestUpgradeDetector implements ManifestUpgradeDetectorFetcher.C |
*/ |
@Override |
public void onGotManifestData(String startUrl, String scopeUrl, String name, String shortName, |
- int displayMode, int orientation, long themeColor, long backgroundColor) { |
+ String iconUrl, int displayMode, int orientation, long themeColor, |
+ long backgroundColor) { |
mFetcher.destroy(); |
mFetcher = null; |
+ if (TextUtils.isEmpty(iconUrl)) { |
+ /** |
+ * The icon URL in the fetched manifest is empty when: |
+ * - The Web Manifest specified an icon at the time that the WebAPK was generated but no |
+ * longer does. |
+ * - The page's favicon was used as the WebAPK's homescreen icon when the WebAPK was |
+ * generated and the Web Manifest still does not specify any icons. |
+ * For the sake of simplicity fetch the old homescreen icon to check whether it has |
+ * changed. We do not support downgrading a WebAPK from using a custom icon (specified |
+ * by the Web Manifest or by the page favicon) to not using an icon at all. |
+ */ |
+ iconUrl = mIconUrl; |
+ } |
+ |
+ mFetchedData = new FetchedManifestData(); |
+ mFetchedData.startUrl = startUrl; |
+ mFetchedData.scopeUrl = scopeUrl; |
+ mFetchedData.name = name; |
+ mFetchedData.shortName = shortName; |
+ mFetchedData.iconUrl = iconUrl; |
+ mFetchedData.displayMode = displayMode; |
+ mFetchedData.orientation = orientation; |
+ mFetchedData.themeColor = themeColor; |
+ mFetchedData.backgroundColor = backgroundColor; |
+ |
+ if (!TextUtils.isEmpty(iconUrl)) { |
+ fetchHomescreenImage(iconUrl, this); |
+ } else { |
+ onFetchedHomescreenImage(null, 0); |
+ } |
+ } |
+ |
+ /** |
+ * Fetches the image at {@link imageUrl} and scales it so that it can be used on the homescreen. |
+ * In dedicated function for the sake of tests. |
+ */ |
+ protected void fetchHomescreenImage( |
+ String imageUrl, ShortcutHelper.FetchHomescreenImageCallback callback) { |
+ ShortcutHelper.fetchHomescreenImage(mTab.getWebContents(), imageUrl, callback); |
+ } |
+ |
+ @Override |
+ public void onFetchedHomescreenImage(Bitmap bitmap, long murmur2Hash) { |
+ mFetchedData.icon = bitmap; |
+ mFetchedData.iconMurmur2Hash = murmur2Hash; |
+ |
// TODO(hanxi): crbug.com/627824. Validate whether the new WebappInfo is |
// WebAPK-compatible. |
- final WebappInfo newInfo = WebappInfo.create(mWebappInfo.id(), startUrl, |
- scopeUrl, mWebappInfo.encodedIcon(), name, shortName, displayMode, orientation, |
- mWebappInfo.source(), themeColor, backgroundColor, mWebappInfo.isIconGenerated(), |
- mWebappInfo.webApkPackageName(), mWebappInfo.webManifestUri().toString()); |
- if (requireUpgrade(newInfo)) { |
+ |
+ if (requireUpgrade(mFetchedData)) { |
upgrade(); |
} |
@@ -114,27 +215,37 @@ public class ManifestUpgradeDetector implements ManifestUpgradeDetectorFetcher.C |
} |
/** |
- * Checks whether the WebAPK needs to be upgraded provided the new Web Manifest info. |
+ * Checks whether the WebAPK needs to be upgraded provided the fetched manifest data. |
*/ |
- private boolean requireUpgrade(WebappInfo newInfo) { |
- boolean scopeMatch = mWebappInfo.scopeUri().equals(newInfo.scopeUri()); |
+ private boolean requireUpgrade(FetchedManifestData fetchedData) { |
+ /** |
+ * Only check whether icon URL differs if Chrome was able to fetch the bitmap at the icon |
+ * URL (no 404). |
+ */ |
+ if (fetchedData.icon != null) { |
+ if (!TextUtils.equals(mIconUrl, fetchedData.iconUrl) |
+ || mIconMurmur2Hash != fetchedData.iconMurmur2Hash) { |
+ return true; |
+ } |
+ } |
+ |
+ boolean scopeMatch = mWebappInfo.scopeUri().toString().equals(fetchedData.scopeUrl); |
if (!scopeMatch) { |
// Sometimes the scope doesn't match due to a missing "/" at the end of the scope URL. |
// Print log to find such cases. |
Log.d(TAG, "Needs to request update since the scope from WebappInfo (%s) doesn't match" |
+ "the one fetched from Web Manifest(%s).", mWebappInfo.scopeUri().toString(), |
- newInfo.scopeUri().toString()); |
+ fetchedData.scopeUrl); |
return true; |
} |
- // TODO(hanxi): Add icon comparison. |
- if (!TextUtils.equals(mStartUrl, newInfo.uri().toString()) |
- || !TextUtils.equals(mWebappInfo.shortName(), newInfo.shortName()) |
- || !TextUtils.equals(mWebappInfo.name(), newInfo.name()) |
- || mWebappInfo.backgroundColor() != newInfo.backgroundColor() |
- || mWebappInfo.themeColor() != newInfo.themeColor() |
- || mWebappInfo.orientation() != newInfo.orientation() |
- || mWebappInfo.displayMode() != newInfo.displayMode()) { |
+ if (!TextUtils.equals(mStartUrl, fetchedData.startUrl) |
+ || !TextUtils.equals(mWebappInfo.shortName(), fetchedData.shortName) |
+ || !TextUtils.equals(mWebappInfo.name(), fetchedData.name) |
+ || mWebappInfo.backgroundColor() != fetchedData.backgroundColor |
+ || mWebappInfo.themeColor() != fetchedData.themeColor |
+ || mWebappInfo.orientation() != fetchedData.orientation |
+ || mWebappInfo.displayMode() != fetchedData.displayMode) { |
return true; |
} |