Commit ff026011 authored by Peter E Conn's avatar Peter E Conn Committed by Commit Bot

👹 Add adaptable homescreen shortcuts for maskable icons.

The Add to Homescreen feature will now consider "maskable" icons from
the web app manifest [1]. If a maskable icon is present and we are on
an Android O+ device, turn that "maskable" icon into an Android Adaptive
icon [2].

The icon is scaled to ensure that the safe zone is preserved [3].

[1]: https://w3c.github.io/manifest/#purpose-member
[2]: https://developer.android.com/guide/practices/ui_guidelines/icon_design_adaptive
[3]: https://github.com/w3c/manifest/issues/555#issuecomment-404097653

Bug: 904354
Change-Id: Iedfe61ace0fda4ca7f3ed3da6a5d07f2bff81443
Reviewed-on: https://chromium-review.googlesource.com/c/1341998
Commit-Queue: Peter Conn <peconn@chromium.org>
Reviewed-by: default avatarMatt Giuca <mgiuca@chromium.org>
Reviewed-by: default avatarPeter Beverloo <peter@chromium.org>
Reviewed-by: default avatarYaron Friedman <yfriedman@chromium.org>
Reviewed-by: default avatarDominick Ng <dominickn@chromium.org>
Cr-Commit-Position: refs/heads/master@{#612218}
parent 57673cf7
...@@ -108,6 +108,30 @@ public class ShortcutHelper { ...@@ -108,6 +108,30 @@ public class ShortcutHelper {
private static final float GENERATED_ICON_PADDING_RATIO = 1.0f / 12.0f; private static final float GENERATED_ICON_PADDING_RATIO = 1.0f / 12.0f;
private static final float GENERATED_ICON_FONT_SIZE_RATIO = 1.0f / 3.0f; private static final float GENERATED_ICON_FONT_SIZE_RATIO = 1.0f / 3.0f;
// Constants for figuring out the amount of padding required to transform a web manifest
// maskable icon to an Android adaptive icon.
//
// The web standard for maskable icons specifies a larger safe zone inside the icon
// than Android adaptive icons define. Therefore we need to pad the image so that
// the maskable icon's safe zone is reduced to the dimensions expected by Android. See
// https://github.com/w3c/manifest/issues/555#issuecomment-404097653.
//
// The *_RATIO variables give the diameter of the safe zone divided by the width of the icon.
// Sources:
// - https://www.w3.org/TR/appmanifest/#icon-masks
// - https://medium.com/google-design/designing-adaptive-icons-515af294c783
//
// We subtract 1 from the scaling factor to give the amount we need to increase by, then divide
// it by two to get the amount of padding that we will add to both sides.
private static final float MASKABLE_SAFE_ZONE_RATIO = 4.0f / 5.0f;
private static final float ADAPTABLE_SAFE_ZONE_RATIO = 66.0f / 108.0f;
private static final float MASKABLE_TO_ADAPTABLE_SCALING_FACTOR =
MASKABLE_SAFE_ZONE_RATIO / ADAPTABLE_SAFE_ZONE_RATIO;
private static final float MASKABLE_ICON_PADDING_RATIO =
(MASKABLE_TO_ADAPTABLE_SCALING_FACTOR - 1.0f) / 2.0f;
// True when Android O's ShortcutManager.requestPinShortcut() is supported. // True when Android O's ShortcutManager.requestPinShortcut() is supported.
private static boolean sIsRequestPinShortcutSupported; private static boolean sIsRequestPinShortcutSupported;
...@@ -120,13 +144,15 @@ public class ShortcutHelper { ...@@ -120,13 +144,15 @@ public class ShortcutHelper {
public static class Delegate { public static class Delegate {
/** /**
* Request Android to add a shortcut to the home screen. * Request Android to add a shortcut to the home screen.
* @param title Title of the shortcut. * @param title Title of the shortcut.
* @param icon Image that represents the shortcut. * @param icon Image that represents the shortcut.
* @param intent Intent to fire when the shortcut is activated. * @param iconAdaptable Whether to create an Android Adaptable icon.
* @param shortcutIntent Intent to fire when the shortcut is activated.
*/ */
public void addShortcutToHomescreen(String title, Bitmap icon, Intent shortcutIntent) { public void addShortcutToHomescreen(
String title, Bitmap icon, boolean iconAdaptable, Intent shortcutIntent) {
if (isRequestPinShortcutSupported()) { if (isRequestPinShortcutSupported()) {
addShortcutWithShortcutManager(title, icon, shortcutIntent); addShortcutWithShortcutManager(title, icon, iconAdaptable, shortcutIntent);
return; return;
} }
Intent intent = createAddToHomeIntent(title, icon, shortcutIntent); Intent intent = createAddToHomeIntent(title, icon, shortcutIntent);
...@@ -160,9 +186,9 @@ public class ShortcutHelper { ...@@ -160,9 +186,9 @@ public class ShortcutHelper {
@CalledByNative @CalledByNative
private static void addWebapp(final String id, final String url, final String scopeUrl, private static void addWebapp(final String id, final String url, final String scopeUrl,
final String userTitle, final String name, final String shortName, final String iconUrl, final String userTitle, final String name, final String shortName, final String iconUrl,
final Bitmap icon, @WebDisplayMode final int displayMode, final int orientation, final Bitmap icon, boolean iconAdaptable, @WebDisplayMode final int displayMode,
final int source, final long themeColor, final long backgroundColor, final int orientation, final int source, final long themeColor,
final String splashScreenUrl, final long callbackPointer) { final long backgroundColor, final String splashScreenUrl, final long callbackPointer) {
new AsyncTask<Intent>() { new AsyncTask<Intent>() {
@Override @Override
protected Intent doInBackground() { protected Intent doInBackground() {
...@@ -185,7 +211,7 @@ public class ShortcutHelper { ...@@ -185,7 +211,7 @@ public class ShortcutHelper {
} }
@Override @Override
protected void onPostExecute(final Intent resultIntent) { protected void onPostExecute(final Intent resultIntent) {
sDelegate.addShortcutToHomescreen(userTitle, icon, resultIntent); sDelegate.addShortcutToHomescreen(userTitle, icon, iconAdaptable, resultIntent);
// Store the webapp data so that it is accessible without the intent. Once this // Store the webapp data so that it is accessible without the intent. Once this
// process is complete, call back to native code to start the splash image // process is complete, call back to native code to start the splash image
...@@ -208,14 +234,14 @@ public class ShortcutHelper { ...@@ -208,14 +234,14 @@ public class ShortcutHelper {
*/ */
@SuppressWarnings("unused") @SuppressWarnings("unused")
@CalledByNative @CalledByNative
private static void addShortcut( private static void addShortcut(String id, String url, String userTitle, Bitmap icon,
String id, String url, String userTitle, Bitmap icon, int source) { boolean iconAdaptable, int source) {
Context context = ContextUtils.getApplicationContext(); Context context = ContextUtils.getApplicationContext();
final Intent shortcutIntent = createShortcutIntent(url); final Intent shortcutIntent = createShortcutIntent(url);
shortcutIntent.putExtra(EXTRA_ID, id); shortcutIntent.putExtra(EXTRA_ID, id);
shortcutIntent.putExtra(EXTRA_SOURCE, source); shortcutIntent.putExtra(EXTRA_SOURCE, source);
shortcutIntent.setPackage(context.getPackageName()); shortcutIntent.setPackage(context.getPackageName());
sDelegate.addShortcutToHomescreen(userTitle, icon, shortcutIntent); sDelegate.addShortcutToHomescreen(userTitle, icon, iconAdaptable, shortcutIntent);
if (shouldShowToastWhenAddingShortcut()) { if (shouldShowToastWhenAddingShortcut()) {
showAddedToHomescreenToast(userTitle); showAddedToHomescreenToast(userTitle);
} }
...@@ -223,14 +249,17 @@ public class ShortcutHelper { ...@@ -223,14 +249,17 @@ public class ShortcutHelper {
@TargetApi(Build.VERSION_CODES.O) @TargetApi(Build.VERSION_CODES.O)
private static void addShortcutWithShortcutManager( private static void addShortcutWithShortcutManager(
String title, Bitmap icon, Intent shortcutIntent) { String title, Bitmap bitmap, boolean isMaskableIcon, Intent shortcutIntent) {
String id = shortcutIntent.getStringExtra(ShortcutHelper.EXTRA_ID); String id = shortcutIntent.getStringExtra(ShortcutHelper.EXTRA_ID);
Context context = ContextUtils.getApplicationContext(); Context context = ContextUtils.getApplicationContext();
Icon icon = isMaskableIcon ? Icon.createWithAdaptiveBitmap(bitmap)
: Icon.createWithBitmap(bitmap);
ShortcutInfo shortcutInfo = new ShortcutInfo.Builder(context, id) ShortcutInfo shortcutInfo = new ShortcutInfo.Builder(context, id)
.setShortLabel(title) .setShortLabel(title)
.setLongLabel(title) .setLongLabel(title)
.setIcon(Icon.createWithBitmap(icon)) .setIcon(icon)
.setIntent(shortcutIntent) .setIntent(shortcutIntent)
.build(); .build();
try { try {
...@@ -410,12 +439,12 @@ public class ShortcutHelper { ...@@ -410,12 +439,12 @@ public class ShortcutHelper {
* Adapts a website's icon (e.g. favicon or touch icon) to make it suitable for the home screen. * Adapts a website's icon (e.g. favicon or touch icon) to make it suitable for the home screen.
* This involves adding padding if the icon is a full sized square. * This involves adding padding if the icon is a full sized square.
* *
* @param context Context used to create the intent.
* @param webIcon The website's favicon or touch icon. * @param webIcon The website's favicon or touch icon.
* @param maskable Whether the icon is suitable for creating an adaptive icon.
* @return Bitmap Either the touch-icon or the newly created favicon. * @return Bitmap Either the touch-icon or the newly created favicon.
*/ */
@CalledByNative @CalledByNative
public static Bitmap createHomeScreenIconFromWebIcon(Bitmap webIcon) { public static Bitmap createHomeScreenIconFromWebIcon(Bitmap webIcon, boolean maskable) {
// getLauncherLargeIconSize() is just a guess at the launcher icon size, and is often // getLauncherLargeIconSize() is just a guess at the launcher icon size, and is often
// wrong -- the launcher can show icons at any size it pleases. Instead of resizing the // wrong -- the launcher can show icons at any size it pleases. Instead of resizing the
// icon to the supposed launcher size and then having the launcher resize the icon again, // icon to the supposed launcher size and then having the launcher resize the icon again,
...@@ -426,18 +455,21 @@ public class ShortcutHelper { ...@@ -426,18 +455,21 @@ public class ShortcutHelper {
int maxInnerSize = Math.round(am.getLauncherLargeIconSize() * MAX_INNER_SIZE_RATIO); int maxInnerSize = Math.round(am.getLauncherLargeIconSize() * MAX_INNER_SIZE_RATIO);
int innerSize = Math.min(maxInnerSize, Math.max(webIcon.getWidth(), webIcon.getHeight())); int innerSize = Math.min(maxInnerSize, Math.max(webIcon.getWidth(), webIcon.getHeight()));
int outerSize = innerSize;
Rect innerBounds = new Rect(0, 0, innerSize, innerSize); Rect innerBounds = new Rect(0, 0, innerSize, innerSize);
int padding = 0;
// Draw the icon with padding around it if all four corners are not transparent. Otherwise,
// don't add padding. if (maskable) {
if (shouldPadIcon(webIcon)) { // See comments for MASKABLE_ICON_PADDING_RATIO.
int padding = Math.round(ICON_PADDING_RATIO * innerSize); padding = Math.round(MASKABLE_ICON_PADDING_RATIO * innerSize);
outerSize += 2 * padding; } else if (shouldPadIcon(webIcon)) {
innerBounds.offset(padding, padding); // Draw the icon with padding around it if all four corners are not transparent.
padding = Math.round(ICON_PADDING_RATIO * innerSize);
} }
Bitmap bitmap = null; int outerSize = 2 * padding + innerSize;
innerBounds.offset(padding, padding);
Bitmap bitmap;
try { try {
bitmap = Bitmap.createBitmap(outerSize, outerSize, Bitmap.Config.ARGB_8888); bitmap = Bitmap.createBitmap(outerSize, outerSize, Bitmap.Config.ARGB_8888);
} catch (OutOfMemoryError e) { } catch (OutOfMemoryError e) {
......
...@@ -4,9 +4,12 @@ ...@@ -4,9 +4,12 @@
package org.chromium.chrome.browser.webapps; package org.chromium.chrome.browser.webapps;
import android.annotation.TargetApi;
import android.app.Activity; import android.app.Activity;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.drawable.Icon;
import android.os.Build;
import android.support.v7.app.AlertDialog; import android.support.v7.app.AlertDialog;
import android.text.Editable; import android.text.Editable;
import android.text.TextUtils; import android.text.TextUtils;
...@@ -229,13 +232,27 @@ public class AddToHomescreenDialog implements View.OnClickListener { ...@@ -229,13 +232,27 @@ public class AddToHomescreenDialog implements View.OnClickListener {
/** /**
* Called when the home screen icon is available. Must be called after onUserTitleAvailable(). * Called when the home screen icon is available. Must be called after onUserTitleAvailable().
* @param icon Icon to use in the launcher. * @param icon that will be used in the launcher.
*/ */
public void onIconAvailable(Bitmap icon) { public void onIconAvailable(Bitmap icon) {
mProgressBarView.setVisibility(View.GONE);
mIconView.setVisibility(View.VISIBLE);
mIconView.setImageBitmap(icon); mIconView.setImageBitmap(icon);
setIconAvailable();
}
/**
* Called when the home screen icon is available and was generated to be an Android adaptable
* icon. Must be called after onUserTitleAvailable().
* @param icon that will be used in the launcher.
*/
@TargetApi(Build.VERSION_CODES.O)
public void onAdaptableIconAvailable(Bitmap icon) {
mIconView.setImageIcon(Icon.createWithAdaptiveBitmap(icon));
setIconAvailable();
}
private void setIconAvailable() {
mProgressBarView.setVisibility(View.GONE);
mIconView.setVisibility(View.VISIBLE);
mHasIcon = true; mHasIcon = true;
updateAddButtonEnabledState(); updateAddButtonEnabledState();
} }
......
...@@ -6,6 +6,7 @@ package org.chromium.chrome.browser.webapps; ...@@ -6,6 +6,7 @@ package org.chromium.chrome.browser.webapps;
import android.app.Activity; import android.app.Activity;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.os.Build;
import android.text.TextUtils; import android.text.TextUtils;
import org.chromium.base.annotations.CalledByNative; import org.chromium.base.annotations.CalledByNative;
...@@ -93,8 +94,13 @@ public class AddToHomescreenManager implements AddToHomescreenDialog.Delegate { ...@@ -93,8 +94,13 @@ public class AddToHomescreenManager implements AddToHomescreenDialog.Delegate {
} }
@CalledByNative @CalledByNative
private void onIconAvailable(Bitmap icon) { private void onIconAvailable(Bitmap icon, boolean iconAdaptable) {
mDialog.onIconAvailable(icon); if (iconAdaptable && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
mDialog.onAdaptableIconAvailable(icon);
} else {
assert !iconAdaptable : "Adaptive icons should not be provided pre-Android O.";
mDialog.onIconAvailable(icon);
}
} }
private native long nativeInitializeAndStart(WebContents webContents); private native long nativeInitializeAndStart(WebContents webContents);
......
...@@ -189,7 +189,8 @@ public class AppBannerManagerTest { ...@@ -189,7 +189,8 @@ public class AppBannerManagerTest {
AppBannerManager.setIsSupported(true); AppBannerManager.setIsSupported(true);
ShortcutHelper.setDelegateForTests(new ShortcutHelper.Delegate() { ShortcutHelper.setDelegateForTests(new ShortcutHelper.Delegate() {
@Override @Override
public void addShortcutToHomescreen(String title, Bitmap icon, Intent shortcutIntent) { public void addShortcutToHomescreen(
String title, Bitmap icon, boolean iconAdaptive, Intent shortcutIntent) {
// Ignore to prevent adding homescreen shortcuts. // Ignore to prevent adding homescreen shortcuts.
} }
}); });
......
...@@ -7,6 +7,7 @@ package org.chromium.chrome.browser.webapps; ...@@ -7,6 +7,7 @@ package org.chromium.chrome.browser.webapps;
import android.app.Activity; import android.app.Activity;
import android.content.Intent; import android.content.Intent;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.os.Build;
import android.support.test.filters.SmallTest; import android.support.test.filters.SmallTest;
import android.text.TextUtils; import android.text.TextUtils;
...@@ -18,6 +19,7 @@ import org.junit.runner.RunWith; ...@@ -18,6 +19,7 @@ import org.junit.runner.RunWith;
import org.chromium.base.ThreadUtils; import org.chromium.base.ThreadUtils;
import org.chromium.base.test.util.CommandLineFlags; import org.chromium.base.test.util.CommandLineFlags;
import org.chromium.base.test.util.DisableIf;
import org.chromium.base.test.util.Feature; import org.chromium.base.test.util.Feature;
import org.chromium.base.test.util.Restriction; import org.chromium.base.test.util.Restriction;
import org.chromium.base.test.util.RetryOnFailure; import org.chromium.base.test.util.RetryOnFailure;
...@@ -82,14 +84,23 @@ public class AddToHomescreenManagerTest { ...@@ -82,14 +84,23 @@ public class AddToHomescreenManagerTest {
+ "<title>" + META_APP_NAME_PAGE_TITLE + "</title>" + "<title>" + META_APP_NAME_PAGE_TITLE + "</title>"
+ "</head><body>Webapp capable</body></html>"); + "</head><body>Webapp capable</body></html>");
private static final String NON_MASKABLE_MANIFEST_TEST_PAGE_PATH =
"/chrome/test/data/banners/manifest_test_page.html";
private static final String MASKABLE_MANIFEST_TEST_PAGE_PATH =
"/chrome/test/data/banners/manifest_test_page.html?manifest=manifest_maskable.json";
private static final String MANIFEST_TEST_PAGE_TITLE = "Web app banner test page";
private static class TestShortcutHelperDelegate extends ShortcutHelper.Delegate { private static class TestShortcutHelperDelegate extends ShortcutHelper.Delegate {
public String mRequestedShortcutTitle; public String mRequestedShortcutTitle;
public Intent mRequestedShortcutIntent; public Intent mRequestedShortcutIntent;
public boolean mRequestedShortcutAdaptable;
@Override @Override
public void addShortcutToHomescreen(String title, Bitmap icon, Intent shortcutIntent) { public void addShortcutToHomescreen(
String title, Bitmap icon, boolean iconAdaptable, Intent shortcutIntent) {
mRequestedShortcutTitle = title; mRequestedShortcutTitle = title;
mRequestedShortcutIntent = shortcutIntent; mRequestedShortcutIntent = shortcutIntent;
mRequestedShortcutAdaptable = iconAdaptable;
} }
@Override @Override
...@@ -100,6 +111,7 @@ public class AddToHomescreenManagerTest { ...@@ -100,6 +111,7 @@ public class AddToHomescreenManagerTest {
public void clearRequestedShortcutData() { public void clearRequestedShortcutData() {
mRequestedShortcutTitle = null; mRequestedShortcutTitle = null;
mRequestedShortcutIntent = null; mRequestedShortcutIntent = null;
mRequestedShortcutAdaptable = false;
} }
} }
...@@ -197,6 +209,31 @@ public class AddToHomescreenManagerTest { ...@@ -197,6 +209,31 @@ public class AddToHomescreenManagerTest {
Assert.assertEquals(mActivity.getPackageName(), newLaunchIntent.getPackage()); Assert.assertEquals(mActivity.getPackageName(), newLaunchIntent.getPackage());
} }
@Test
@SmallTest
@Feature("{Webapp}")
// The test manifest fulfills the requirements of a WebAPK so disable WebAPKs to force plain old
// add to home screen.
@CommandLineFlags.Add({"disable-features=ImprovedA2HS"})
@DisableIf.Build(sdk_is_less_than = Build.VERSION_CODES.O)
public void testAddAdaptableShortcut() throws Exception {
// Test the baseline of no adaptive icon.
loadUrl(mTestServerRule.getServer().getURL(NON_MASKABLE_MANIFEST_TEST_PAGE_PATH),
MANIFEST_TEST_PAGE_TITLE);
addShortcutToTab(mTab, "", true);
Assert.assertFalse(mShortcutHelperDelegate.mRequestedShortcutAdaptable);
mShortcutHelperDelegate.clearRequestedShortcutData();
// Test the adaptive icon.
loadUrl(mTestServerRule.getServer().getURL(MASKABLE_MANIFEST_TEST_PAGE_PATH),
MANIFEST_TEST_PAGE_TITLE);
addShortcutToTab(mTab, "", true);
Assert.assertTrue(mShortcutHelperDelegate.mRequestedShortcutAdaptable);
}
@Test @Test
@SmallTest @SmallTest
@Feature("{Webapp}") @Feature("{Webapp}")
......
...@@ -79,6 +79,7 @@ void GetHomescreenIconAndSplashImageSizes() { ...@@ -79,6 +79,7 @@ void GetHomescreenIconAndSplashImageSizes() {
void AddWebappWithSkBitmap(const ShortcutInfo& info, void AddWebappWithSkBitmap(const ShortcutInfo& info,
const std::string& webapp_id, const std::string& webapp_id,
const SkBitmap& icon_bitmap, const SkBitmap& icon_bitmap,
bool is_icon_maskable,
const base::Closure& splash_image_callback) { const base::Closure& splash_image_callback) {
// Send the data to the Java side to create the shortcut. // Send the data to the Java side to create the shortcut.
JNIEnv* env = base::android::AttachCurrentThread(); JNIEnv* env = base::android::AttachCurrentThread();
...@@ -113,8 +114,8 @@ void AddWebappWithSkBitmap(const ShortcutInfo& info, ...@@ -113,8 +114,8 @@ void AddWebappWithSkBitmap(const ShortcutInfo& info,
Java_ShortcutHelper_addWebapp( Java_ShortcutHelper_addWebapp(
env, java_webapp_id, java_url, java_scope_url, java_user_title, java_name, env, java_webapp_id, java_url, java_scope_url, java_user_title, java_name,
java_short_name, java_best_primary_icon_url, java_bitmap, info.display, java_short_name, java_best_primary_icon_url, java_bitmap,
info.orientation, info.source, is_icon_maskable, info.display, info.orientation, info.source,
OptionalSkColorToJavaColor(info.theme_color), OptionalSkColorToJavaColor(info.theme_color),
OptionalSkColorToJavaColor(info.background_color), java_splash_screen_url, OptionalSkColorToJavaColor(info.background_color), java_splash_screen_url,
callback_pointer); callback_pointer);
...@@ -123,7 +124,8 @@ void AddWebappWithSkBitmap(const ShortcutInfo& info, ...@@ -123,7 +124,8 @@ void AddWebappWithSkBitmap(const ShortcutInfo& info,
// Adds a shortcut which opens in a browser tab to the launcher. // Adds a shortcut which opens in a browser tab to the launcher.
void AddShortcutWithSkBitmap(const ShortcutInfo& info, void AddShortcutWithSkBitmap(const ShortcutInfo& info,
const std::string& id, const std::string& id,
const SkBitmap& icon_bitmap) { const SkBitmap& icon_bitmap,
bool is_icon_maskable) {
JNIEnv* env = base::android::AttachCurrentThread(); JNIEnv* env = base::android::AttachCurrentThread();
ScopedJavaLocalRef<jstring> java_id = ScopedJavaLocalRef<jstring> java_id =
base::android::ConvertUTF8ToJavaString(env, id); base::android::ConvertUTF8ToJavaString(env, id);
...@@ -136,7 +138,7 @@ void AddShortcutWithSkBitmap(const ShortcutInfo& info, ...@@ -136,7 +138,7 @@ void AddShortcutWithSkBitmap(const ShortcutInfo& info,
java_bitmap = gfx::ConvertToJavaBitmap(&icon_bitmap); java_bitmap = gfx::ConvertToJavaBitmap(&icon_bitmap);
Java_ShortcutHelper_addShortcut(env, java_id, java_url, java_user_title, Java_ShortcutHelper_addShortcut(env, java_id, java_url, java_user_title,
java_bitmap, info.source); java_bitmap, is_icon_maskable, info.source);
} }
} // anonymous namespace } // anonymous namespace
...@@ -171,19 +173,20 @@ std::unique_ptr<ShortcutInfo> ShortcutHelper::CreateShortcutInfo( ...@@ -171,19 +173,20 @@ std::unique_ptr<ShortcutInfo> ShortcutHelper::CreateShortcutInfo(
void ShortcutHelper::AddToLauncherWithSkBitmap( void ShortcutHelper::AddToLauncherWithSkBitmap(
content::WebContents* web_contents, content::WebContents* web_contents,
const ShortcutInfo& info, const ShortcutInfo& info,
const SkBitmap& icon_bitmap) { const SkBitmap& icon_bitmap,
bool is_icon_maskable) {
std::string webapp_id = base::GenerateGUID(); std::string webapp_id = base::GenerateGUID();
if (info.display == blink::kWebDisplayModeStandalone || if (info.display == blink::kWebDisplayModeStandalone ||
info.display == blink::kWebDisplayModeFullscreen || info.display == blink::kWebDisplayModeFullscreen ||
info.display == blink::kWebDisplayModeMinimalUi) { info.display == blink::kWebDisplayModeMinimalUi) {
AddWebappWithSkBitmap( AddWebappWithSkBitmap(
info, webapp_id, icon_bitmap, info, webapp_id, icon_bitmap, is_icon_maskable,
base::Bind(&ShortcutHelper::FetchSplashScreenImage, web_contents, base::Bind(&ShortcutHelper::FetchSplashScreenImage, web_contents,
info.splash_image_url, info.ideal_splash_image_size_in_px, info.splash_image_url, info.ideal_splash_image_size_in_px,
info.minimum_splash_image_size_in_px, webapp_id)); info.minimum_splash_image_size_in_px, webapp_id));
return; return;
} }
AddShortcutWithSkBitmap(info, webapp_id, icon_bitmap); AddShortcutWithSkBitmap(info, webapp_id, icon_bitmap, is_icon_maskable);
} }
void ShortcutHelper::ShowWebApkInstallInProgressToast() { void ShortcutHelper::ShowWebApkInstallInProgressToast() {
...@@ -255,6 +258,7 @@ void ShortcutHelper::StoreWebappSplashImage(const std::string& webapp_id, ...@@ -255,6 +258,7 @@ void ShortcutHelper::StoreWebappSplashImage(const std::string& webapp_id,
// static // static
SkBitmap ShortcutHelper::FinalizeLauncherIconInBackground( SkBitmap ShortcutHelper::FinalizeLauncherIconInBackground(
const SkBitmap& bitmap, const SkBitmap& bitmap,
bool is_icon_maskable,
const GURL& url, const GURL& url,
bool* is_generated) { bool* is_generated) {
base::AssertLongCPUWorkAllowed(); base::AssertLongCPUWorkAllowed();
...@@ -268,8 +272,8 @@ SkBitmap ShortcutHelper::FinalizeLauncherIconInBackground( ...@@ -268,8 +272,8 @@ SkBitmap ShortcutHelper::FinalizeLauncherIconInBackground(
bitmap.height())) { bitmap.height())) {
ScopedJavaLocalRef<jobject> java_bitmap = ScopedJavaLocalRef<jobject> java_bitmap =
gfx::ConvertToJavaBitmap(&bitmap); gfx::ConvertToJavaBitmap(&bitmap);
result = result = Java_ShortcutHelper_createHomeScreenIconFromWebIcon(
Java_ShortcutHelper_createHomeScreenIconFromWebIcon(env, java_bitmap); env, java_bitmap, is_icon_maskable);
} }
} }
......
...@@ -42,7 +42,8 @@ class ShortcutHelper { ...@@ -42,7 +42,8 @@ class ShortcutHelper {
// added depends on the properties in |info|. // added depends on the properties in |info|.
static void AddToLauncherWithSkBitmap(content::WebContents* web_contents, static void AddToLauncherWithSkBitmap(content::WebContents* web_contents,
const ShortcutInfo& info, const ShortcutInfo& info,
const SkBitmap& icon_bitmap); const SkBitmap& icon_bitmap,
bool is_icon_maskable);
// Shows toast notifying user that a WebAPK install is already in progress // Shows toast notifying user that a WebAPK install is already in progress
// when user tries to queue a new install for the same WebAPK. // when user tries to queue a new install for the same WebAPK.
...@@ -85,6 +86,7 @@ class ShortcutHelper { ...@@ -85,6 +86,7 @@ class ShortcutHelper {
// |is_generated| will be set to |true|. // |is_generated| will be set to |true|.
// Must be called on a background worker thread. // Must be called on a background worker thread.
static SkBitmap FinalizeLauncherIconInBackground(const SkBitmap& icon, static SkBitmap FinalizeLauncherIconInBackground(const SkBitmap& icon,
bool is_icon_maskable,
const GURL& url, const GURL& url,
bool* is_generated); bool* is_generated);
......
...@@ -95,8 +95,10 @@ void WebApkInstallService::OnFinishedInstall( ...@@ -95,8 +95,10 @@ void WebApkInstallService::OnFinishedInstall(
if (!web_contents) if (!web_contents)
return; return;
// TODO(https://crbug.com/861643): Support maskable icons here.
ShortcutHelper::AddToLauncherWithSkBitmap(web_contents, shortcut_info, ShortcutHelper::AddToLauncherWithSkBitmap(web_contents, shortcut_info,
primary_icon); primary_icon,
/*is_icon_maskable=*/false);
} }
} }
......
...@@ -46,10 +46,8 @@ GURL GetShortcutUrl(const content::WebContents* web_contents) { ...@@ -46,10 +46,8 @@ GURL GetShortcutUrl(const content::WebContents* web_contents) {
} }
bool DoesAndroidSupportMaskableIcons() { bool DoesAndroidSupportMaskableIcons() {
// TODO(peconn): Enable once Chrome on Android correctly handles maskable
// icons (https://crbug.com/904354).
return base::android::BuildInfo::GetInstance()->sdk_int() >= return base::android::BuildInfo::GetInstance()->sdk_int() >=
base::android::SDK_VERSION_OREO && false; base::android::SDK_VERSION_OREO;
} }
InstallableParams ParamsToPerformManifestAndIconFetch() { InstallableParams ParamsToPerformManifestAndIconFetch() {
...@@ -77,10 +75,11 @@ InstallableParams ParamsToPerformInstallableCheck() { ...@@ -77,10 +75,11 @@ InstallableParams ParamsToPerformInstallableCheck() {
// - the generated icon // - the generated icon
// - whether |icon| was used in generating the launcher icon // - whether |icon| was used in generating the launcher icon
std::pair<SkBitmap, bool> CreateLauncherIconInBackground(const GURL& start_url, std::pair<SkBitmap, bool> CreateLauncherIconInBackground(const GURL& start_url,
const SkBitmap& icon) { const SkBitmap& icon,
bool maskable) {
bool is_generated = false; bool is_generated = false;
SkBitmap primary_icon = ShortcutHelper::FinalizeLauncherIconInBackground( SkBitmap primary_icon = ShortcutHelper::FinalizeLauncherIconInBackground(
icon, start_url, &is_generated); icon, maskable, start_url, &is_generated);
return std::make_pair(primary_icon, is_generated); return std::make_pair(primary_icon, is_generated);
} }
...@@ -99,7 +98,8 @@ std::pair<SkBitmap, bool> CreateLauncherIconFromFaviconInBackground( ...@@ -99,7 +98,8 @@ std::pair<SkBitmap, bool> CreateLauncherIconFromFaviconInBackground(
gfx::PNGCodec::Decode(bitmap_result.bitmap_data->front(), gfx::PNGCodec::Decode(bitmap_result.bitmap_data->front(),
bitmap_result.bitmap_data->size(), &decoded); bitmap_result.bitmap_data->size(), &decoded);
} }
return CreateLauncherIconInBackground(start_url, decoded); return CreateLauncherIconInBackground(start_url, decoded,
/* maskable */ false);
} }
void RecordAddToHomescreenDialogDuration(base::TimeDelta duration) { void RecordAddToHomescreenDialogDuration(base::TimeDelta duration) {
...@@ -238,6 +238,7 @@ void AddToHomescreenDataFetcher::OnDidGetManifestAndIcons( ...@@ -238,6 +238,7 @@ void AddToHomescreenDataFetcher::OnDidGetManifestAndIcons(
} }
raw_primary_icon_ = *data.primary_icon; raw_primary_icon_ = *data.primary_icon;
has_maskable_primary_icon_ = data.has_maskable_primary_icon;
shortcut_info_.best_primary_icon_url = data.primary_icon_url; shortcut_info_.best_primary_icon_url = data.primary_icon_url;
// Save the splash screen URL for the later download. // Save the splash screen URL for the later download.
...@@ -346,7 +347,8 @@ void AddToHomescreenDataFetcher::CreateLauncherIcon(const SkBitmap& icon) { ...@@ -346,7 +347,8 @@ void AddToHomescreenDataFetcher::CreateLauncherIcon(const SkBitmap& icon) {
FROM_HERE, FROM_HERE,
{base::MayBlock(), base::TaskPriority::USER_VISIBLE, {base::MayBlock(), base::TaskPriority::USER_VISIBLE,
base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN}, base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
base::BindOnce(&CreateLauncherIconInBackground, shortcut_info_.url, icon), base::BindOnce(&CreateLauncherIconInBackground, shortcut_info_.url, icon,
has_maskable_primary_icon_),
base::BindOnce(&AddToHomescreenDataFetcher::NotifyObserver, base::BindOnce(&AddToHomescreenDataFetcher::NotifyObserver,
weak_ptr_factory_.GetWeakPtr())); weak_ptr_factory_.GetWeakPtr()));
} }
......
...@@ -66,6 +66,7 @@ class AddToHomescreenDataFetcher : public content::WebContentsObserver { ...@@ -66,6 +66,7 @@ class AddToHomescreenDataFetcher : public content::WebContentsObserver {
const SkBitmap& badge_icon() const { return badge_icon_; } const SkBitmap& badge_icon() const { return badge_icon_; }
const SkBitmap& primary_icon() const { return primary_icon_; } const SkBitmap& primary_icon() const { return primary_icon_; }
ShortcutInfo& shortcut_info() { return shortcut_info_; } ShortcutInfo& shortcut_info() { return shortcut_info_; }
bool has_maskable_primary_icon() const { return has_maskable_primary_icon_; }
private: private:
// Called to stop the timeout timer. // Called to stop the timeout timer.
...@@ -99,6 +100,7 @@ class AddToHomescreenDataFetcher : public content::WebContentsObserver { ...@@ -99,6 +100,7 @@ class AddToHomescreenDataFetcher : public content::WebContentsObserver {
SkBitmap badge_icon_; SkBitmap badge_icon_;
SkBitmap primary_icon_; SkBitmap primary_icon_;
ShortcutInfo shortcut_info_; ShortcutInfo shortcut_info_;
bool has_maskable_primary_icon_;
base::CancelableTaskTracker favicon_task_tracker_; base::CancelableTaskTracker favicon_task_tracker_;
base::OneShotTimer data_timeout_timer_; base::OneShotTimer data_timeout_timer_;
......
...@@ -77,9 +77,10 @@ void AddToHomescreenManager::AddToHomescreen( ...@@ -77,9 +77,10 @@ void AddToHomescreenManager::AddToHomescreen(
base::string16 user_title = base::string16 user_title =
base::android::ConvertJavaStringToUTF16(env, j_user_title); base::android::ConvertJavaStringToUTF16(env, j_user_title);
data_fetcher_->shortcut_info().user_title = user_title; data_fetcher_->shortcut_info().user_title = user_title;
ShortcutHelper::AddToLauncherWithSkBitmap(web_contents, ShortcutHelper::AddToLauncherWithSkBitmap(
data_fetcher_->shortcut_info(), web_contents, data_fetcher_->shortcut_info(),
data_fetcher_->primary_icon()); data_fetcher_->primary_icon(),
data_fetcher_->has_maskable_primary_icon());
} }
// Fire the appinstalled event and do install time logging. // Fire the appinstalled event and do install time logging.
...@@ -143,5 +144,6 @@ void AddToHomescreenManager::OnDataAvailable(const ShortcutInfo& info, ...@@ -143,5 +144,6 @@ void AddToHomescreenManager::OnDataAvailable(const ShortcutInfo& info,
java_bitmap = gfx::ConvertToJavaBitmap(&primary_icon); java_bitmap = gfx::ConvertToJavaBitmap(&primary_icon);
JNIEnv* env = base::android::AttachCurrentThread(); JNIEnv* env = base::android::AttachCurrentThread();
Java_AddToHomescreenManager_onIconAvailable(env, java_ref_, java_bitmap); Java_AddToHomescreenManager_onIconAvailable(
env, java_ref_, java_bitmap, data_fetcher_->has_maskable_primary_icon());
} }
...@@ -318,8 +318,10 @@ void AppBannerUiDelegateAndroid::InstallLegacyWebApp( ...@@ -318,8 +318,10 @@ void AppBannerUiDelegateAndroid::InstallLegacyWebApp(
AppBannerSettingsHelper::RecordBannerInstallEvent( AppBannerSettingsHelper::RecordBannerInstallEvent(
web_contents, shortcut_info_->url.spec(), AppBannerSettingsHelper::WEB); web_contents, shortcut_info_->url.spec(), AppBannerSettingsHelper::WEB);
// TODO(https://crbug.com/861643): Support maskable icons here.
ShortcutHelper::AddToLauncherWithSkBitmap(web_contents, *shortcut_info_, ShortcutHelper::AddToLauncherWithSkBitmap(web_contents, *shortcut_info_,
primary_icon_); primary_icon_,
/*is_icon_maskable=*/false);
} }
void AppBannerUiDelegateAndroid::SendBannerAccepted() { void AppBannerUiDelegateAndroid::SendBannerAccepted() {
......
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