Commit 842f0969 authored by Calder Kitagawa's avatar Calder Kitagawa Committed by Commit Bot

[WebAPK] Improve splash icon logic.

pkotwicz@ pointed out that res/mipmap is actually an alias for
res/mipmap-mdpi. This resulted in all icons being way too large and
appear with some fidelity issues. In testing I believed these larger
icons to be correct but in reality they should have been smaller. To
fix this we could move the icons into the mipmap-nodpi folder.
However, this prevents them from scaling even when aliased. i.e. if
on an xxhdpi device all we have is an mdpi icon the following
scenario occurs; drawable-*dpi/ has no valid splash_icon.xml so we
fall back to the mipmap-*dpi/app_icon.xml here we find that only the
mdpi icon is available. One would think this then scales up to an
appropriate size on drawable-xxhdpi but instead Android refuses to
scale it as it is in the mipmap-nodpi as the double lookup does not
change the fact it is nodpi. pkotwicz@ then suggested we use Scale
XML resources, these solve the problem but require the minting
server to manually compute the scaling at mint time. Instead I
propose the following solution:

- Restore the state for app_icon.png files prior to CL
  https://chromium-review.googlesource.com/1128256
- Introduce a single splash_icon.xml in drawable-xxxhpdi. This
  will reference mipmap/app_icon and essentially be identical to
  the current behavior of just using the app_icon (this is just a
  placeholder).
- Add a boolean to the metadata of the AndroidManifest.xml
  indicating whether we were able to package larger images on
  the server. (This is so xxhpdi can use these images rather than
  only using the slightly larger xxxhdpi icon).
- Use ApiCompatibilityUtils#getDrawableForDensity(). This gives
  us the image for a specific density or scales the nearest
  available one if there isn't one available. This allows us
  to control which size of image we show. We can then impose the
  following lookup scheme based on the current device density.

Tested on xhdpi, hdpi and xxhdpi running N, O, P

| Current Density | App Icon Density for Splash Icon |
| mdpi            | xhdpi                            |
| hdpi            | xxhdpi                           |
| xhdpi           | xxxhdpi                          |
| xxhdpi          | xxxhdpi or larger if provided    |
| xxxhdpi         | xxxhdpi or larger if provided    |

Bug: 819755
Change-Id: I40fe68375782b29d082193aae0ab1c1af0ae49f2
Reviewed-on: https://chromium-review.googlesource.com/1150297
Commit-Queue: Calder Kitagawa <ckitagawa@chromium.org>
Reviewed-by: default avatarDominick Ng <dominickn@chromium.org>
Reviewed-by: default avatarXi Han <hanxi@chromium.org>
Cr-Commit-Position: refs/heads/master@{#581549}
parent 143a0622
......@@ -12,6 +12,7 @@ import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.DisplayMetrics;
import org.chromium.base.ApiCompatibilityUtils;
import org.chromium.base.ContextUtils;
......@@ -146,8 +147,7 @@ public class WebApkInfo extends WebappInfo {
int badgeIconId = IntentUtils.safeGetInt(bundle, WebApkMetaDataKeys.BADGE_ICON_ID, 0);
Bitmap badgeIcon = decodeBitmapFromDrawable(res, badgeIconId);
int splashIconId = IntentUtils.safeGetInt(bundle, WebApkMetaDataKeys.SPLASH_ID, 0);
Bitmap splashIcon = decodeBitmapFromDrawable(res, splashIconId);
Bitmap splashIcon = decodeSplashIcon(bundle, res, primaryIconId);
return create(WebApkConstants.WEBAPK_ID_PREFIX + webApkPackageName, url, scope,
new Icon(primaryIcon), new Icon(badgeIcon), new Icon(splashIcon), name, shortName,
......@@ -306,6 +306,66 @@ public class WebApkInfo extends WebappInfo {
}
}
/**
* Decodes a bitmap drawable from the WebAPK's resources for a specific density.
*/
private static Bitmap decodeBitmapFromDrawableForDensity(
Resources webApkResources, int resourceId, int density) {
if (resourceId == 0) {
return null;
}
try {
BitmapDrawable bitmapDrawable =
(BitmapDrawable) ApiCompatibilityUtils.getDrawableForDensity(
webApkResources, resourceId, density);
return bitmapDrawable != null ? bitmapDrawable.getBitmap() : null;
} catch (Resources.NotFoundException e) {
return null;
}
}
/**
* Computes the density of app icon to use for the splash screen based on the device density.
*/
private static int computeDensityforSplashIcon(int deviceDensity) {
if (deviceDensity < DisplayMetrics.DENSITY_HIGH) {
return DisplayMetrics.DENSITY_XHIGH;
}
if (deviceDensity < DisplayMetrics.DENSITY_XHIGH) {
return DisplayMetrics.DENSITY_XXHIGH;
}
return DisplayMetrics.DENSITY_XXXHIGH;
}
/**
* Decodes a splash icon with logic dependent on the device's density.
*/
private static Bitmap decodeSplashIcon(
Bundle bundle, Resources webApkResources, int appIconId) {
DisplayMetrics metrics = webApkResources.getDisplayMetrics();
if (metrics == null) {
// We have no basis on which to choose a higher DPI, so default to primary icon.
return null;
}
int deviceDensity = metrics.densityDpi;
int splashIconId = IntentUtils.safeGetInt(bundle, WebApkMetaDataKeys.SPLASH_ID, 0);
boolean hasLargeSplashIcons = IntentUtils.safeGetBoolean(
bundle, WebApkMetaDataKeys.HAS_LARGE_SPLASH_ICONS, false);
if (deviceDensity >= DisplayMetrics.DENSITY_XXHIGH && hasLargeSplashIcons) {
// If the server was able to provide larger splash icons we use those. This can be
// one or both of xxhdpi and xxxhdpi. If only one exists the up/down sampling is only
// for a single density and will still look good.
return decodeBitmapFromDrawable(webApkResources, splashIconId);
}
// If there is no drawable for a density this will down-sample or up-sample whatever app
// icons are provided to fit the target density. This can result in lower graphical
// fidelity; however, Chrome already does this for xxxhdpi icons in most cases so this isn't
// a concern.
return decodeBitmapFromDrawableForDensity(
webApkResources, appIconId, computeDensityforSplashIcon(deviceDensity));
}
/**
* Extracts long value from the WebAPK's meta data.
* @param metaData WebAPK meta data to extract the long from.
......
......@@ -22,6 +22,8 @@ public final class WebApkMetaDataKeys {
public static final String ORIENTATION = "org.chromium.webapk.shell_apk.orientation";
public static final String THEME_COLOR = "org.chromium.webapk.shell_apk.themeColor";
public static final String BACKGROUND_COLOR = "org.chromium.webapk.shell_apk.backgroundColor";
public static final String HAS_LARGE_SPLASH_ICONS =
"org.chromium.webapk.shell_apk.hasLargeSplashIcons";
public static final String ICON_ID = "org.chromium.webapk.shell_apk.iconId";
public static final String SPLASH_ID = "org.chromium.webapk.shell_apk.splashId";
......
......@@ -65,6 +65,7 @@
<meta-data android:name="org.chromium.webapk.shell_apk.orientation" android:value="{{{orientation}}}" />
<meta-data android:name="org.chromium.webapk.shell_apk.themeColor" android:value="{{{theme_color}}}" />
<meta-data android:name="org.chromium.webapk.shell_apk.backgroundColor" android:value="{{{background_color}}}" />
<meta-data android:name="org.chromium.webapk.shell_apk.hasLargeSplashIcons" android:value="{{{has_large_splash_icons}}}" />
<meta-data android:name="org.chromium.webapk.shell_apk.iconId" android:resource="@mipmap/app_icon" />
<meta-data android:name="org.chromium.webapk.shell_apk.splashId" android:resource="@drawable/splash_icon" />
......
......@@ -12,6 +12,7 @@
"orientation": "portrait",
"theme_color": "2147483648L",
"background_color": "2147483648L",
"has_large_splash_icons": "false",
"icon_urls_and_icon_murmur2_hashes": "http://www.pwa.rocks/icon1.png 0 http://www.pwa.rocks/icon2.png 0",
"web_manifest_url": "https://pwa.rocks/pwa.webmanifest",
"version_code": "1",
......
......@@ -12,6 +12,7 @@
"orientation": "portrait",
"theme_color": "2147483648L",
"background_color": "2147483648L",
"has_large_splash_icons": "false",
"icon_urls_and_icon_murmur2_hashes": "http://www.pwa.rocks/icon1.png 0 http://www.pwa.rocks/icon2.png 0",
"web_manifest_url": "https://pwa.rocks/pwa.webmanifest",
"version_code": "1",
......
......@@ -12,6 +12,7 @@
"orientation": "portrait",
"theme_color": "2147483648L",
"background_color": "2147483648L",
"has_large_splash_icons": "false",
"icon_urls_and_icon_murmur2_hashes": "http://www.pwa.rocks/icon1.png 0 http://www.pwa.rocks/icon2.png 0",
"web_manifest_url": "https://pwa.rocks/pwa.webmanifest",
"version_code": "1",
......
......@@ -12,6 +12,7 @@
"orientation": "portrait",
"theme_color": "2147483648L",
"background_color": "2147483648L",
"has_large_splash_icons": "false",
"icon_urls_and_icon_murmur2_hashes": "https://maps.gstatic.com/mapfiles/maps_lite/pwa/icons/maps_pwa_icon_v0920_48x48.png 0 https://maps.gstatic.com/mapfiles/maps_lite/pwa/icons/maps_pwa_icon_v0920_72x72 0",
"web_manifest_url": "https://maps.gstatic.com/tactile/worker/ml.json",
"version_code": "1",
......
<?xml version="1.0" encoding="utf-8"?>
<!-- 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. -->
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
android:src="@mipmap/app_icon_xxhdpi"/>
<?xml version="1.0" encoding="utf-8"?>
<!-- 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. -->
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
android:src="@mipmap/app_icon_xhdpi"/>
<?xml version="1.0" encoding="utf-8"?>
<!-- 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. -->
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
android:src="@mipmap/app_icon_xxxhdpi"/>
<?xml version="1.0" encoding="utf-8"?>
<!-- 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. -->
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
android:src="@mipmap/app_icon_xxxhdpi"/>
......@@ -4,4 +4,4 @@
found in the LICENSE file. -->
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
android:src="@mipmap/app_icon_xxxhdpi"/>
android:src="@mipmap/app_icon"/>
<?xml version="1.0" encoding="utf-8"?>
<!-- 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. -->
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
android:src="@mipmap/app_icon_hdpi"/>
......@@ -4,4 +4,4 @@
found in the LICENSE file. -->
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
android:src="@mipmap/app_icon_hdpi"/>
android:src="@mipmap/app_icon"/>
<?xml version="1.0" encoding="utf-8"?>
<!-- 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. -->
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
android:src="@mipmap/app_icon_mdpi"/>
......@@ -4,4 +4,4 @@
found in the LICENSE file. -->
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
android:src="@mipmap/app_icon_mdpi"/>
android:src="@mipmap/app_icon"/>
<?xml version="1.0" encoding="utf-8"?>
<!-- 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. -->
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
android:src="@mipmap/app_icon_xhdpi"/>
......@@ -4,4 +4,4 @@
found in the LICENSE file. -->
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
android:src="@mipmap/app_icon_xhdpi"/>
android:src="@mipmap/app_icon"/>
<?xml version="1.0" encoding="utf-8"?>
<!-- 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. -->
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
android:src="@mipmap/app_icon_xxhdpi"/>
......@@ -4,4 +4,4 @@
found in the LICENSE file. -->
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
android:src="@mipmap/app_icon_xxhdpi"/>
android:src="@mipmap/app_icon"/>
<?xml version="1.0" encoding="utf-8"?>
<!-- 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. -->
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
android:src="@mipmap/app_icon_xxxhdpi"/>
......@@ -4,4 +4,4 @@
found in the LICENSE file. -->
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
android:src="@mipmap/app_icon_xxxhdpi"/>
android:src="@mipmap/app_icon"/>
......@@ -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 = 47
template_shell_apk_version = 48
# 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|.
......
......@@ -13,6 +13,7 @@
"orientation": "portrait",
"theme_color": "2147483648L",
"background_color": "2147483648L",
"has_large_splash_icons": "false",
"icon_urls_and_icon_murmur2_hashes": "http://www.pwa.rocks/icon1.png 0 http://www.pwa.rocks/icon2.png 0",
"web_manifest_url": "https://pwa.rocks/pwa.webmanifest",
"version_code": "1",
......
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