| Index: chrome/android/java/src/org/chromium/chrome/browser/ShortcutHelper.java
|
| diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ShortcutHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/ShortcutHelper.java
|
| index 6fcc9e915329bc248f897e33a9be34adb53a0853..f16c72f450e5f7e8896d478fb840bbb77b27c342 100644
|
| --- a/chrome/android/java/src/org/chromium/chrome/browser/ShortcutHelper.java
|
| +++ b/chrome/android/java/src/org/chromium/chrome/browser/ShortcutHelper.java
|
| @@ -4,13 +4,17 @@
|
|
|
| package org.chromium.chrome.browser;
|
|
|
| +import android.annotation.SuppressLint;
|
| +import android.annotation.TargetApi;
|
| import android.app.ActivityManager;
|
| import android.content.Context;
|
| import android.content.Intent;
|
| +import android.content.IntentSender;
|
| import android.content.pm.ApplicationInfo;
|
| import android.content.pm.PackageInfo;
|
| import android.content.pm.PackageManager;
|
| import android.content.pm.PackageManager.NameNotFoundException;
|
| +import android.content.pm.ResolveInfo;
|
| import android.graphics.Bitmap;
|
| import android.graphics.BitmapFactory;
|
| import android.graphics.Canvas;
|
| @@ -19,6 +23,7 @@ import android.graphics.Paint;
|
| import android.graphics.Rect;
|
| import android.graphics.drawable.BitmapDrawable;
|
| import android.graphics.drawable.Drawable;
|
| +import android.graphics.drawable.Icon;
|
| import android.net.Uri;
|
| import android.os.AsyncTask;
|
| import android.support.annotation.NonNull;
|
| @@ -26,6 +31,7 @@ import android.text.TextUtils;
|
| import android.util.Base64;
|
|
|
| import org.chromium.base.ApiCompatibilityUtils;
|
| +import org.chromium.base.BuildInfo;
|
| import org.chromium.base.ContextUtils;
|
| import org.chromium.base.Log;
|
| import org.chromium.base.ThreadUtils;
|
| @@ -33,7 +39,6 @@ import org.chromium.base.VisibleForTesting;
|
| import org.chromium.base.annotations.CalledByNative;
|
| import org.chromium.blink_public.platform.WebDisplayMode;
|
| import org.chromium.chrome.R;
|
| -import org.chromium.chrome.browser.webapps.ChromeShortcutManager;
|
| import org.chromium.chrome.browser.webapps.ChromeWebApkHost;
|
| import org.chromium.chrome.browser.webapps.WebApkInfo;
|
| import org.chromium.chrome.browser.webapps.WebappActivity;
|
| @@ -47,6 +52,9 @@ import org.chromium.ui.widget.Toast;
|
| import org.chromium.webapk.lib.client.WebApkValidator;
|
|
|
| import java.io.ByteArrayOutputStream;
|
| +import java.lang.reflect.Constructor;
|
| +import java.lang.reflect.InvocationTargetException;
|
| +import java.lang.reflect.Method;
|
| import java.util.ArrayList;
|
| import java.util.List;
|
|
|
| @@ -88,6 +96,8 @@ public class ShortcutHelper {
|
|
|
| private static final String TAG = "ShortcutHelper";
|
|
|
| + private static final String INSTALL_SHORTCUT = "com.android.launcher.action.INSTALL_SHORTCUT";
|
| +
|
| // The activity class used for launching a WebApk.
|
| private static final String WEBAPK_MAIN_ACTIVITY = "org.chromium.webapk.shell_apk.MainActivity";
|
|
|
| @@ -99,6 +109,15 @@ public class ShortcutHelper {
|
| 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;
|
|
|
| + // True when Android O's ShortcutManager.requestPinShortcut() is supported.
|
| + private static boolean sIsRequestPinShortcutSupported;
|
| +
|
| + // True when it is already checked if ShortcutManager.requestPinShortcut() is supported.
|
| + private static boolean sCheckedIfRequestPinShortcutSupported;
|
| +
|
| + // TODO(martiw): Use 'ShortcutInfo' instead of 'Object' below when compileSdk is bumped to O.
|
| + private static Object sShortcutManager;
|
| +
|
| /** Helper for generating home screen shortcuts. */
|
| public static class Delegate {
|
| /**
|
| @@ -108,8 +127,12 @@ public class ShortcutHelper {
|
| * @param intent Intent to fire when the shortcut is activated.
|
| */
|
| public void addShortcutToHomescreen(String title, Bitmap icon, Intent shortcutIntent) {
|
| - ChromeShortcutManager.getInstance().addShortcutToHomeScreen(
|
| - title, icon, shortcutIntent);
|
| + if (isRequestPinShortcutSupported()) {
|
| + addShortcutWithShortcutManager(title, icon, shortcutIntent);
|
| + return;
|
| + }
|
| + Intent intent = createAddToHomeIntent(title, icon, shortcutIntent);
|
| + ContextUtils.getApplicationContext().sendBroadcast(intent);
|
| }
|
|
|
| /**
|
| @@ -173,7 +196,7 @@ public class ShortcutHelper {
|
| nativeOnWebappDataStored(callbackPointer);
|
| }
|
| });
|
| - if (ChromeShortcutManager.getInstance().shouldShowToastWhenAddingShortcut()) {
|
| + if (shouldShowToastWhenAddingShortcut()) {
|
| showAddedToHomescreenToast(userTitle);
|
| }
|
| }
|
| @@ -192,8 +215,7 @@ public class ShortcutHelper {
|
| Intent i = new Intent();
|
| i.setClassName(packageName, WEBAPK_MAIN_ACTIVITY);
|
| i.addCategory(Intent.CATEGORY_LAUNCHER);
|
| - context.sendBroadcast(
|
| - ChromeShortcutManager.createAddToHomeIntent(shortcutTitle, bitmap, i));
|
| + context.sendBroadcast(createAddToHomeIntent(shortcutTitle, bitmap, i));
|
| } catch (NameNotFoundException e) {
|
| e.printStackTrace();
|
| }
|
| @@ -212,11 +234,59 @@ public class ShortcutHelper {
|
| shortcutIntent.putExtra(EXTRA_SOURCE, source);
|
| shortcutIntent.setPackage(context.getPackageName());
|
| sDelegate.addShortcutToHomescreen(userTitle, icon, shortcutIntent);
|
| - if (ChromeShortcutManager.getInstance().shouldShowToastWhenAddingShortcut()) {
|
| + if (shouldShowToastWhenAddingShortcut()) {
|
| showAddedToHomescreenToast(userTitle);
|
| }
|
| }
|
|
|
| + // TODO(martiw): Use Build.VERSION_CODES.O instead of hardcoded number when it is available.
|
| + @TargetApi(26)
|
| + private static void addShortcutWithShortcutManager(
|
| + String title, Bitmap icon, Intent shortcutIntent) {
|
| + String id = shortcutIntent.getStringExtra(ShortcutHelper.EXTRA_ID);
|
| + Context context = ContextUtils.getApplicationContext();
|
| +
|
| + // The code in the try-block uses reflection in order to compile as it calls APIs newer than
|
| + // our compileSdkVersion of Android. The equivalent code without reflection looks like this:
|
| + // ShortcutInfo shortcutInfo = new ShortcutInfo.Builder(context, id)
|
| + // .setShortLabel(title)
|
| + // .setLongLabel(title)
|
| + // .setIcon(Icon.createWithBitmap(icon))
|
| + // .setIntent(shortcutIntent)
|
| + // .build();
|
| + // sShortcutManager.requestPinShortcut(shortcutInfo, null);
|
| + // TODO(martiw): Remove the following reflection once compileSdk is bumped to O.
|
| + try {
|
| + Class<?> builderClass = Class.forName("android.content.pm.ShortcutInfo$Builder");
|
| + Constructor<?> builderConstructor =
|
| + builderClass.getDeclaredConstructor(Context.class, String.class);
|
| + Object shortcutBuilder = builderConstructor.newInstance(context, id);
|
| +
|
| + Method setShortLabel = builderClass.getMethod("setShortLabel", CharSequence.class);
|
| + setShortLabel.invoke(shortcutBuilder, title);
|
| +
|
| + Method setLongLabel = builderClass.getMethod("setLongLabel", CharSequence.class);
|
| + setLongLabel.invoke(shortcutBuilder, title);
|
| +
|
| + Method setIcon = builderClass.getMethod("setIcon", Icon.class);
|
| + setIcon.invoke(shortcutBuilder, Icon.createWithBitmap(icon));
|
| +
|
| + Method setIntent = builderClass.getMethod("setIntent", Intent.class);
|
| + setIntent.invoke(shortcutBuilder, shortcutIntent);
|
| +
|
| + Method build = builderClass.getMethod("build");
|
| + Object shortcutInfo = build.invoke(shortcutBuilder);
|
| +
|
| + Class<?> ShortcutInfoClass = Class.forName("android.content.pm.ShortcutInfo");
|
| + Method requestPinShortcut = sShortcutManager.getClass().getMethod(
|
| + "requestPinShortcut", ShortcutInfoClass, IntentSender.class);
|
| + requestPinShortcut.invoke(sShortcutManager, shortcutInfo, null);
|
| + } catch (ClassNotFoundException | NoSuchMethodException | InstantiationException
|
| + | InvocationTargetException | IllegalAccessException e) {
|
| + Log.e(TAG, "Error adding shortcut with ShortcutManager:", e);
|
| + }
|
| + }
|
| +
|
| /**
|
| * Show toast to alert user that the shortcut was added to the home screen.
|
| */
|
| @@ -270,6 +340,21 @@ public class ShortcutHelper {
|
| }
|
| }
|
|
|
| + /**
|
| + * Creates an intent that will add a shortcut to the home screen.
|
| + * @param title Title of the shortcut.
|
| + * @param icon Image that represents the shortcut.
|
| + * @param shortcutIntent Intent to fire when the shortcut is activated.
|
| + * @return Intent for the shortcut.
|
| + */
|
| + public static Intent createAddToHomeIntent(String title, Bitmap icon, Intent shortcutIntent) {
|
| + Intent i = new Intent(INSTALL_SHORTCUT);
|
| + i.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
|
| + i.putExtra(Intent.EXTRA_SHORTCUT_NAME, title);
|
| + i.putExtra(Intent.EXTRA_SHORTCUT_ICON, icon);
|
| + return i;
|
| + }
|
| +
|
| /**
|
| * Creates a shortcut to launch a web app on the home screen.
|
| * @param id Id of the web app.
|
| @@ -342,8 +427,15 @@ public class ShortcutHelper {
|
| * Utility method to check if a shortcut can be added to the home screen.
|
| * @return if a shortcut can be added to the home screen under the current profile.
|
| */
|
| + // TODO(crbug.com/635567): Fix this properly.
|
| + @SuppressLint("WrongConstant")
|
| public static boolean isAddToHomeIntentSupported() {
|
| - return ChromeShortcutManager.getInstance().canAddShortcutToHomescreen();
|
| + if (isRequestPinShortcutSupported()) return true;
|
| + PackageManager pm = ContextUtils.getApplicationContext().getPackageManager();
|
| + Intent i = new Intent(INSTALL_SHORTCUT);
|
| + List<ResolveInfo> receivers =
|
| + pm.queryBroadcastReceivers(i, PackageManager.GET_INTENT_FILTERS);
|
| + return !receivers.isEmpty();
|
| }
|
|
|
| /**
|
| @@ -623,6 +715,44 @@ public class ShortcutHelper {
|
| return false;
|
| }
|
|
|
| + private static boolean shouldShowToastWhenAddingShortcut() {
|
| + return !isRequestPinShortcutSupported();
|
| + }
|
| +
|
| + private static boolean isRequestPinShortcutSupported() {
|
| + if (!sCheckedIfRequestPinShortcutSupported) {
|
| + if (BuildInfo.isAtLeastO()) {
|
| + checkIfRequestPinShortcutSupported();
|
| + }
|
| + sCheckedIfRequestPinShortcutSupported = true;
|
| + }
|
| + return sIsRequestPinShortcutSupported;
|
| + }
|
| +
|
| + // TODO(martiw): Use Build.VERSION_CODES.O instead of hardcoded number when it is available.
|
| + @TargetApi(26)
|
| + private static void checkIfRequestPinShortcutSupported() {
|
| + // The code in the try-block uses reflection in order to compile as it calls APIs newer than
|
| + // our target version of Android. The equivalent code without reflection is as follows:
|
| + // sShortcutManager =
|
| + // ContextUtils.getApplicationContext().getSystemService(ShortcutManager.class);
|
| + // sIsRequestPinShortcutSupported = sShortcutManager.isRequestPinShortcutSupported();
|
| + // TODO(martiw): Remove the following reflection once compileSdk is bumped to O.
|
| + try {
|
| + Class<?> ShortcutManagerClass = Class.forName("android.content.pm.ShortcutManager");
|
| + sShortcutManager =
|
| + ContextUtils.getApplicationContext().getSystemService(ShortcutManagerClass);
|
| +
|
| + Method isRequestPinShortcutSupported =
|
| + ShortcutManagerClass.getMethod("isRequestPinShortcutSupported");
|
| + sIsRequestPinShortcutSupported =
|
| + (boolean) isRequestPinShortcutSupported.invoke(sShortcutManager);
|
| + } catch (ClassNotFoundException | NoSuchMethodException | InvocationTargetException
|
| + | IllegalAccessException e) {
|
| + Log.e(TAG, "Error checking if RequestPinShortcut is supported:", e);
|
| + }
|
| + }
|
| +
|
| private static int getSizeFromResourceInPx(Context context, int resource) {
|
| return Math.round(context.getResources().getDimension(resource));
|
| }
|
|
|