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)); |
} |