Index: chrome/android/java/src/org/chromium/chrome/browser/webapps/ChromeShortcutManager.java |
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/ChromeShortcutManager.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/ChromeShortcutManager.java |
index 8b47b3d55e2c551c83c4acb7840691b4d7eef40c..dd85d5449aa7ae2a1caf83ee63d760f96de9e73e 100644 |
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/ChromeShortcutManager.java |
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/ChromeShortcutManager.java |
@@ -5,30 +5,50 @@ |
package org.chromium.chrome.browser.webapps; |
import android.annotation.SuppressLint; |
+import android.annotation.TargetApi; |
+import android.content.Context; |
import android.content.Intent; |
+import android.content.IntentSender; |
import android.content.pm.PackageManager; |
import android.content.pm.ResolveInfo; |
import android.graphics.Bitmap; |
+import android.graphics.drawable.Icon; |
+import org.chromium.base.BuildInfo; |
import org.chromium.base.ContextUtils; |
-import org.chromium.chrome.browser.AppHooks; |
+import org.chromium.base.Log; |
+import org.chromium.chrome.browser.ShortcutHelper; |
+import java.lang.reflect.Constructor; |
+import java.lang.reflect.InvocationTargetException; |
+import java.lang.reflect.Method; |
import java.util.List; |
/** |
* This class handles the adding of shortcuts to the Android Home Screen. |
*/ |
public class ChromeShortcutManager { |
+ private static final String TAG = "ChromeShortcutMgr"; |
+ |
// There is no public string defining this intent so if Home changes the value, we |
// have to update this string. |
private static final String INSTALL_SHORTCUT = "com.android.launcher.action.INSTALL_SHORTCUT"; |
private static ChromeShortcutManager sInstance; |
- /* Returns the singleton instance of ChromeShortcutManager, creating it if needed. */ |
+ // True when Android O's ShortcutManager.requestPinShortcut() is supported. |
+ private boolean mIsRequestPinShortcutSupported; |
+ |
+ // TODO(martiw): Use 'ShortcutInfo' instead of 'Object' below when compileSdk is bumped to O. |
+ private Object mShortcutManager; |
+ |
+ /** |
+ * Returns the singleton instance of ChromeShortcutManager, creating it if needed. |
+ */ |
+ // TODO(martiw): change this to private when ChromeShortcutManagerInternal is gone. |
public static ChromeShortcutManager getInstance() { |
if (sInstance == null) { |
- sInstance = AppHooks.get().createChromeShortcutManager(); |
+ sInstance = new ChromeShortcutManager(); |
} |
return sInstance; |
} |
@@ -48,6 +68,37 @@ public class ChromeShortcutManager { |
return i; |
} |
+ // TODO(martiw): Make this private when ChromeShortcutManagerInternal is removed. |
+ public ChromeShortcutManager() { |
+ if (BuildInfo.isAtLeastO()) { |
+ checkIfRequestPinShortcutSupported(); |
+ } |
+ } |
+ |
+ // TODO(martiw): Use Build.VERSION_CODES.O instead of hardcoded number when it is available. |
+ @TargetApi(26) |
+ private 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: |
+ // mShortcutManager = |
+ // ContextUtils.getApplicationContext().getSystemService(ShortcutManager.class); |
+ // mIsRequestPinShortcutSupported = mShortcutManager.isRequestPinShortcutSupported(); |
+ // TODO(martiw): Remove the following reflection once compileSdk is bumped to O. |
+ try { |
+ Class<?> ShortcutManagerClass = Class.forName("android.content.pm.ShortcutManager"); |
+ mShortcutManager = |
+ ContextUtils.getApplicationContext().getSystemService(ShortcutManagerClass); |
+ |
+ Method isRequestPinShortcutSupported = |
+ ShortcutManagerClass.getMethod("isRequestPinShortcutSupported"); |
+ mIsRequestPinShortcutSupported = |
+ (boolean) isRequestPinShortcutSupported.invoke(mShortcutManager); |
+ } catch (ClassNotFoundException | NoSuchMethodException | InvocationTargetException |
+ | IllegalAccessException e) { |
+ Log.e(TAG, "Error checking if RequestPinShortcut is supported:", e); |
+ } |
+ } |
+ |
/** |
* Add a shortcut to the home screen. |
* @param title Title of the shortcut. |
@@ -55,13 +106,65 @@ public class ChromeShortcutManager { |
* @param shortcutIntent Intent to fire when the shortcut is activated. |
*/ |
public void addShortcutToHomeScreen(String title, Bitmap icon, Intent shortcutIntent) { |
+ if (mIsRequestPinShortcutSupported) { |
+ addShortcutWithShortcutManager(title, icon, shortcutIntent); |
+ return; |
+ } |
Intent intent = createAddToHomeIntent(title, icon, shortcutIntent); |
ContextUtils.getApplicationContext().sendBroadcast(intent); |
} |
+ // TODO(martiw): Use Build.VERSION_CODES.O instead of hardcoded number when it is available. |
+ @TargetApi(26) |
+ private 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(); |
+ // mShortcutManager.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 = mShortcutManager.getClass().getMethod( |
+ "requestPinShortcut", ShortcutInfoClass, IntentSender.class); |
+ requestPinShortcut.invoke(mShortcutManager, shortcutInfo, null); |
+ } catch (ClassNotFoundException | NoSuchMethodException | InstantiationException |
+ | InvocationTargetException | IllegalAccessException e) { |
+ Log.e(TAG, "Error adding shortcut with ShortcutManager:", e); |
+ } |
+ } |
+ |
// TODO(crbug.com/635567): Fix this properly. |
@SuppressLint("WrongConstant") |
public boolean canAddShortcutToHomescreen() { |
+ if (mIsRequestPinShortcutSupported) return true; |
PackageManager pm = ContextUtils.getApplicationContext().getPackageManager(); |
Intent i = new Intent(INSTALL_SHORTCUT); |
List<ResolveInfo> receivers = |
@@ -70,6 +173,6 @@ public class ChromeShortcutManager { |
} |
public boolean shouldShowToastWhenAddingShortcut() { |
- return true; |
+ return !mIsRequestPinShortcutSupported; |
} |
} |