InstallShortcutReceiver.java revision dff0bfeb901e767ca9b57f9b8051c7fd077626e9
131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project/* 231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project * Copyright (C) 2008 The Android Open Source Project 331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project * 431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License"); 531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project * you may not use this file except in compliance with the License. 631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project * You may obtain a copy of the License at 731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project * 831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project * http://www.apache.org/licenses/LICENSE-2.0 931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project * 1031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project * Unless required by applicable law or agreed to in writing, software 1131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS, 1231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project * See the License for the specific language governing permissions and 1431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project * limitations under the License. 1531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project */ 1631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project 17325dc23624160689e59fbac708cf6f222b20d025Daniel Sandlerpackage com.android.launcher3; 1831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project 1931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Projectimport android.content.BroadcastReceiver; 2031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Projectimport android.content.Context; 2131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Projectimport android.content.Intent; 22f0c6ae0e35d3e020db55b5b826955da66b14b7f6Winson Chungimport android.content.SharedPreferences; 23c3a747ac245d6175a6838230d8b01c618555e062Winson Chungimport android.content.pm.ActivityInfo; 24c3a747ac245d6175a6838230d8b01c618555e062Winson Chungimport android.content.pm.PackageManager; 2548c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurkaimport android.graphics.Bitmap; 2648c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurkaimport android.graphics.BitmapFactory; 27dff0bfeb901e767ca9b57f9b8051c7fd077626e9Nilesh Agrawalimport android.text.TextUtils; 2848c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurkaimport android.util.Base64; 2948c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurkaimport android.util.Log; 30d93a7d1716b06cd65f45dfc16232277d0d57c04dRomain Guyimport android.widget.Toast; 3131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project 3234c2e6cf7af328aba25c98158161dbad15ae986dMichael Jurkaimport org.json.JSONObject; 3334c2e6cf7af328aba25c98158161dbad15ae986dMichael Jurkaimport org.json.JSONStringer; 3434c2e6cf7af328aba25c98158161dbad15ae986dMichael Jurkaimport org.json.JSONTokener; 3534c2e6cf7af328aba25c98158161dbad15ae986dMichael Jurka 36e428e292c27e650ab4de76b78728ba55019cd282Winson Chungimport java.util.ArrayList; 37f0c6ae0e35d3e020db55b5b826955da66b14b7f6Winson Chungimport java.util.HashSet; 38f561bdf68dd304e1d7c3f7796621a4cc39ca15f2Winson Chungimport java.util.Iterator; 39f0c6ae0e35d3e020db55b5b826955da66b14b7f6Winson Chungimport java.util.Set; 40e428e292c27e650ab4de76b78728ba55019cd282Winson Chung 4131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Projectpublic class InstallShortcutReceiver extends BroadcastReceiver { 424e871a217b286775dc6c07feb6d9f1d7a7e914beBjorn Bringert private static final String TAG = "InstallShortcutReceiver"; 434e871a217b286775dc6c07feb6d9f1d7a7e914beBjorn Bringert private static final boolean DBG = false; 444e871a217b286775dc6c07feb6d9f1d7a7e914beBjorn Bringert 45a9abd0e0bdedb5cbbd12b84cb83037a735e79a20Winson Chung public static final String ACTION_INSTALL_SHORTCUT = 4694d6768c65929efa21bb893fdef7f269d65da3c3Winson Chung "com.android.launcher.action.INSTALL_SHORTCUT"; 47f0c6ae0e35d3e020db55b5b826955da66b14b7f6Winson Chung 4848c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka public static final String DATA_INTENT_KEY = "intent.data"; 4948c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka public static final String LAUNCH_INTENT_KEY = "intent.launch"; 5048c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka public static final String NAME_KEY = "name"; 5148c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka public static final String ICON_KEY = "icon"; 5248c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka public static final String ICON_RESOURCE_NAME_KEY = "iconResource"; 5348c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka public static final String ICON_RESOURCE_PACKAGE_NAME_KEY = "iconResourcePackage"; 5448c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka // The set of shortcuts that are pending install 5548c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka public static final String APPS_PENDING_INSTALL = "apps_to_install"; 5648c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka 57f0c6ae0e35d3e020db55b5b826955da66b14b7f6Winson Chung public static final int NEW_SHORTCUT_BOUNCE_DURATION = 450; 58997a92348a6d6e061737399321393449c16cd4d8Winson Chung public static final int NEW_SHORTCUT_STAGGER_DELAY = 85; 59f0c6ae0e35d3e020db55b5b826955da66b14b7f6Winson Chung 60f0c6ae0e35d3e020db55b5b826955da66b14b7f6Winson Chung private static final int INSTALL_SHORTCUT_SUCCESSFUL = 0; 61f0c6ae0e35d3e020db55b5b826955da66b14b7f6Winson Chung private static final int INSTALL_SHORTCUT_IS_DUPLICATE = -1; 6251ed5b9bc05c6eb37dcb3fe38fa021f1044c0f88Romain Guy 6368846fdce6c01bbe474bd0c8307e1ccaac161cbcWinson Chung // A mime-type representing shortcut data 6468846fdce6c01bbe474bd0c8307e1ccaac161cbcWinson Chung public static final String SHORTCUT_MIMETYPE = 65325dc23624160689e59fbac708cf6f222b20d025Daniel Sandler "com.android.launcher3/shortcut"; 6668846fdce6c01bbe474bd0c8307e1ccaac161cbcWinson Chung 67c7700af21f7b1df939fce22f4ab5072b3ea29b66Michael Jurka private static Object sLock = new Object(); 68c7700af21f7b1df939fce22f4ab5072b3ea29b66Michael Jurka 69c7700af21f7b1df939fce22f4ab5072b3ea29b66Michael Jurka private static void addToStringSet(SharedPreferences sharedPrefs, 70c7700af21f7b1df939fce22f4ab5072b3ea29b66Michael Jurka SharedPreferences.Editor editor, String key, String value) { 71c7700af21f7b1df939fce22f4ab5072b3ea29b66Michael Jurka Set<String> strings = sharedPrefs.getStringSet(key, null); 72c7700af21f7b1df939fce22f4ab5072b3ea29b66Michael Jurka if (strings == null) { 73c7700af21f7b1df939fce22f4ab5072b3ea29b66Michael Jurka strings = new HashSet<String>(0); 74c7700af21f7b1df939fce22f4ab5072b3ea29b66Michael Jurka } else { 75c7700af21f7b1df939fce22f4ab5072b3ea29b66Michael Jurka strings = new HashSet<String>(strings); 76c7700af21f7b1df939fce22f4ab5072b3ea29b66Michael Jurka } 77c7700af21f7b1df939fce22f4ab5072b3ea29b66Michael Jurka strings.add(value); 78c7700af21f7b1df939fce22f4ab5072b3ea29b66Michael Jurka editor.putStringSet(key, strings); 79c7700af21f7b1df939fce22f4ab5072b3ea29b66Michael Jurka } 8048c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka 8148c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka private static void addToInstallQueue( 8248c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka SharedPreferences sharedPrefs, PendingInstallShortcutInfo info) { 8348c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka synchronized(sLock) { 8448c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka try { 8548c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka JSONStringer json = new JSONStringer() 8648c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka .object() 8748c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka .key(DATA_INTENT_KEY).value(info.data.toUri(0)) 8848c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka .key(LAUNCH_INTENT_KEY).value(info.launchIntent.toUri(0)) 8948c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka .key(NAME_KEY).value(info.name); 9048c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka if (info.icon != null) { 9148c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka byte[] iconByteArray = ItemInfo.flattenBitmap(info.icon); 9248c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka json = json.key(ICON_KEY).value( 9348c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka Base64.encodeToString( 9448c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka iconByteArray, 0, iconByteArray.length, Base64.DEFAULT)); 9548c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka } 9648c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka if (info.iconResource != null) { 9748c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka json = json.key(ICON_RESOURCE_NAME_KEY).value(info.iconResource.resourceName); 9848c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka json = json.key(ICON_RESOURCE_PACKAGE_NAME_KEY) 9948c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka .value(info.iconResource.packageName); 10048c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka } 10148c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka json = json.endObject(); 10248c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka SharedPreferences.Editor editor = sharedPrefs.edit(); 1034e871a217b286775dc6c07feb6d9f1d7a7e914beBjorn Bringert if (DBG) Log.d(TAG, "Adding to APPS_PENDING_INSTALL: " + json); 10448c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka addToStringSet(sharedPrefs, editor, APPS_PENDING_INSTALL, json.toString()); 10548c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka editor.commit(); 10648c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka } catch (org.json.JSONException e) { 1074e871a217b286775dc6c07feb6d9f1d7a7e914beBjorn Bringert Log.d(TAG, "Exception when adding shortcut: " + e); 10848c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka } 10948c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka } 11048c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka } 11148c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka 112780fe59a7af8b12fbdcd7f6841edaa7f2c2e019dWinson Chung public static void removeFromInstallQueue(SharedPreferences sharedPrefs, 113780fe59a7af8b12fbdcd7f6841edaa7f2c2e019dWinson Chung ArrayList<String> packageNames) { 114df95eb12a4d288ffa143a0c3db60e7e98f3e03b9Winson Chung if (packageNames.isEmpty()) { 115df95eb12a4d288ffa143a0c3db60e7e98f3e03b9Winson Chung return; 116df95eb12a4d288ffa143a0c3db60e7e98f3e03b9Winson Chung } 117780fe59a7af8b12fbdcd7f6841edaa7f2c2e019dWinson Chung synchronized(sLock) { 118780fe59a7af8b12fbdcd7f6841edaa7f2c2e019dWinson Chung Set<String> strings = sharedPrefs.getStringSet(APPS_PENDING_INSTALL, null); 1194e871a217b286775dc6c07feb6d9f1d7a7e914beBjorn Bringert if (DBG) { 1204e871a217b286775dc6c07feb6d9f1d7a7e914beBjorn Bringert Log.d(TAG, "APPS_PENDING_INSTALL: " + strings 1214e871a217b286775dc6c07feb6d9f1d7a7e914beBjorn Bringert + ", removing packages: " + packageNames); 1224e871a217b286775dc6c07feb6d9f1d7a7e914beBjorn Bringert } 123780fe59a7af8b12fbdcd7f6841edaa7f2c2e019dWinson Chung if (strings != null) { 124780fe59a7af8b12fbdcd7f6841edaa7f2c2e019dWinson Chung Set<String> newStrings = new HashSet<String>(strings); 1254e871a217b286775dc6c07feb6d9f1d7a7e914beBjorn Bringert Iterator<String> newStringsIter = newStrings.iterator(); 1264e871a217b286775dc6c07feb6d9f1d7a7e914beBjorn Bringert while (newStringsIter.hasNext()) { 1274e871a217b286775dc6c07feb6d9f1d7a7e914beBjorn Bringert String json = newStringsIter.next(); 128780fe59a7af8b12fbdcd7f6841edaa7f2c2e019dWinson Chung try { 129780fe59a7af8b12fbdcd7f6841edaa7f2c2e019dWinson Chung JSONObject object = (JSONObject) new JSONTokener(json).nextValue(); 130780fe59a7af8b12fbdcd7f6841edaa7f2c2e019dWinson Chung Intent launchIntent = Intent.parseUri(object.getString(LAUNCH_INTENT_KEY), 0); 131780fe59a7af8b12fbdcd7f6841edaa7f2c2e019dWinson Chung String pn = launchIntent.getPackage(); 132780fe59a7af8b12fbdcd7f6841edaa7f2c2e019dWinson Chung if (pn == null) { 133780fe59a7af8b12fbdcd7f6841edaa7f2c2e019dWinson Chung pn = launchIntent.getComponent().getPackageName(); 134780fe59a7af8b12fbdcd7f6841edaa7f2c2e019dWinson Chung } 135780fe59a7af8b12fbdcd7f6841edaa7f2c2e019dWinson Chung if (packageNames.contains(pn)) { 1364e871a217b286775dc6c07feb6d9f1d7a7e914beBjorn Bringert newStringsIter.remove(); 137780fe59a7af8b12fbdcd7f6841edaa7f2c2e019dWinson Chung } 138780fe59a7af8b12fbdcd7f6841edaa7f2c2e019dWinson Chung } catch (org.json.JSONException e) { 1394e871a217b286775dc6c07feb6d9f1d7a7e914beBjorn Bringert Log.d(TAG, "Exception reading shortcut to remove: " + e); 140780fe59a7af8b12fbdcd7f6841edaa7f2c2e019dWinson Chung } catch (java.net.URISyntaxException e) { 1414e871a217b286775dc6c07feb6d9f1d7a7e914beBjorn Bringert Log.d(TAG, "Exception reading shortcut to remove: " + e); 142780fe59a7af8b12fbdcd7f6841edaa7f2c2e019dWinson Chung } 143780fe59a7af8b12fbdcd7f6841edaa7f2c2e019dWinson Chung } 144780fe59a7af8b12fbdcd7f6841edaa7f2c2e019dWinson Chung sharedPrefs.edit().putStringSet(APPS_PENDING_INSTALL, 145780fe59a7af8b12fbdcd7f6841edaa7f2c2e019dWinson Chung new HashSet<String>(newStrings)).commit(); 146780fe59a7af8b12fbdcd7f6841edaa7f2c2e019dWinson Chung } 147780fe59a7af8b12fbdcd7f6841edaa7f2c2e019dWinson Chung } 148780fe59a7af8b12fbdcd7f6841edaa7f2c2e019dWinson Chung } 149780fe59a7af8b12fbdcd7f6841edaa7f2c2e019dWinson Chung 15048c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka private static ArrayList<PendingInstallShortcutInfo> getAndClearInstallQueue( 15148c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka SharedPreferences sharedPrefs) { 15248c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka synchronized(sLock) { 15348c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka Set<String> strings = sharedPrefs.getStringSet(APPS_PENDING_INSTALL, null); 1544e871a217b286775dc6c07feb6d9f1d7a7e914beBjorn Bringert if (DBG) Log.d(TAG, "Getting and clearing APPS_PENDING_INSTALL: " + strings); 15548c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka if (strings == null) { 15648c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka return new ArrayList<PendingInstallShortcutInfo>(); 15748c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka } 15848c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka ArrayList<PendingInstallShortcutInfo> infos = 15948c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka new ArrayList<PendingInstallShortcutInfo>(); 16048c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka for (String json : strings) { 16148c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka try { 16248c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka JSONObject object = (JSONObject) new JSONTokener(json).nextValue(); 16348c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka Intent data = Intent.parseUri(object.getString(DATA_INTENT_KEY), 0); 164780fe59a7af8b12fbdcd7f6841edaa7f2c2e019dWinson Chung Intent launchIntent = 165780fe59a7af8b12fbdcd7f6841edaa7f2c2e019dWinson Chung Intent.parseUri(object.getString(LAUNCH_INTENT_KEY), 0); 16648c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka String name = object.getString(NAME_KEY); 16748c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka String iconBase64 = object.optString(ICON_KEY); 16848c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka String iconResourceName = object.optString(ICON_RESOURCE_NAME_KEY); 16948c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka String iconResourcePackageName = 17048c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka object.optString(ICON_RESOURCE_PACKAGE_NAME_KEY); 17148c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka if (iconBase64 != null && !iconBase64.isEmpty()) { 17248c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka byte[] iconArray = Base64.decode(iconBase64, Base64.DEFAULT); 17348c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka Bitmap b = BitmapFactory.decodeByteArray(iconArray, 0, iconArray.length); 17448c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka data.putExtra(Intent.EXTRA_SHORTCUT_ICON, b); 17548c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka } else if (iconResourceName != null && !iconResourceName.isEmpty()) { 17648c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka Intent.ShortcutIconResource iconResource = 17748c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka new Intent.ShortcutIconResource(); 17848c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka iconResource.resourceName = iconResourceName; 17948c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka iconResource.packageName = iconResourcePackageName; 18048c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka data.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, iconResource); 18148c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka } 18248c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka data.putExtra(Intent.EXTRA_SHORTCUT_INTENT, launchIntent); 18348c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka PendingInstallShortcutInfo info = 18448c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka new PendingInstallShortcutInfo(data, name, launchIntent); 18548c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka infos.add(info); 18648c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka } catch (org.json.JSONException e) { 1874e871a217b286775dc6c07feb6d9f1d7a7e914beBjorn Bringert Log.d(TAG, "Exception reading shortcut to add: " + e); 18848c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka } catch (java.net.URISyntaxException e) { 1894e871a217b286775dc6c07feb6d9f1d7a7e914beBjorn Bringert Log.d(TAG, "Exception reading shortcut to add: " + e); 19048c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka } 19148c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka } 19248c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka sharedPrefs.edit().putStringSet(APPS_PENDING_INSTALL, new HashSet<String>()).commit(); 19348c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka return infos; 19448c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka } 19548c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka } 19648c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka 197f561bdf68dd304e1d7c3f7796621a4cc39ca15f2Winson Chung // Determines whether to defer installing shortcuts immediately until 198f561bdf68dd304e1d7c3f7796621a4cc39ca15f2Winson Chung // processAllPendingInstalls() is called. 199f561bdf68dd304e1d7c3f7796621a4cc39ca15f2Winson Chung private static boolean mUseInstallQueue = false; 200f561bdf68dd304e1d7c3f7796621a4cc39ca15f2Winson Chung 201f561bdf68dd304e1d7c3f7796621a4cc39ca15f2Winson Chung private static class PendingInstallShortcutInfo { 202f561bdf68dd304e1d7c3f7796621a4cc39ca15f2Winson Chung Intent data; 203f561bdf68dd304e1d7c3f7796621a4cc39ca15f2Winson Chung Intent launchIntent; 204f561bdf68dd304e1d7c3f7796621a4cc39ca15f2Winson Chung String name; 20548c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka Bitmap icon; 20648c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka Intent.ShortcutIconResource iconResource; 207f561bdf68dd304e1d7c3f7796621a4cc39ca15f2Winson Chung 208f561bdf68dd304e1d7c3f7796621a4cc39ca15f2Winson Chung public PendingInstallShortcutInfo(Intent rawData, String shortcutName, 209f561bdf68dd304e1d7c3f7796621a4cc39ca15f2Winson Chung Intent shortcutIntent) { 210f561bdf68dd304e1d7c3f7796621a4cc39ca15f2Winson Chung data = rawData; 211f561bdf68dd304e1d7c3f7796621a4cc39ca15f2Winson Chung name = shortcutName; 212f561bdf68dd304e1d7c3f7796621a4cc39ca15f2Winson Chung launchIntent = shortcutIntent; 213f561bdf68dd304e1d7c3f7796621a4cc39ca15f2Winson Chung } 214f561bdf68dd304e1d7c3f7796621a4cc39ca15f2Winson Chung } 21531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project 21631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project public void onReceive(Context context, Intent data) { 21751ed5b9bc05c6eb37dcb3fe38fa021f1044c0f88Romain Guy if (!ACTION_INSTALL_SHORTCUT.equals(data.getAction())) { 21851ed5b9bc05c6eb37dcb3fe38fa021f1044c0f88Romain Guy return; 21951ed5b9bc05c6eb37dcb3fe38fa021f1044c0f88Romain Guy } 22051ed5b9bc05c6eb37dcb3fe38fa021f1044c0f88Romain Guy 2214e871a217b286775dc6c07feb6d9f1d7a7e914beBjorn Bringert if (DBG) Log.d(TAG, "Got INSTALL_SHORTCUT: " + data.toUri(0)); 2224e871a217b286775dc6c07feb6d9f1d7a7e914beBjorn Bringert 223f561bdf68dd304e1d7c3f7796621a4cc39ca15f2Winson Chung Intent intent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT); 224c3a747ac245d6175a6838230d8b01c618555e062Winson Chung if (intent == null) { 225c3a747ac245d6175a6838230d8b01c618555e062Winson Chung return; 226c3a747ac245d6175a6838230d8b01c618555e062Winson Chung } 227dff0bfeb901e767ca9b57f9b8051c7fd077626e9Nilesh Agrawal 228c3a747ac245d6175a6838230d8b01c618555e062Winson Chung // This name is only used for comparisons and notifications, so fall back to activity name 229c3a747ac245d6175a6838230d8b01c618555e062Winson Chung // if not supplied 2302c1afde5cd60eb13f05aeae0c170e697493ef307Winson Chung String name = ensureValidName(context, intent, 2312c1afde5cd60eb13f05aeae0c170e697493ef307Winson Chung data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME)).toString(); 23248c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka Bitmap icon = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON); 23348c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka Intent.ShortcutIconResource iconResource = 23448c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE); 23548c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka 236de0fb8fbf11caac382589ab464b50d528942273aWinson Chung // Queue the item up for adding if launcher has not loaded properly yet 237c88dceb0b567ab96418e73cf2f6b197ba2646cb3Winson Chung LauncherAppState.setApplicationContext(context.getApplicationContext()); 238892c74d460ad98c6306420e1127c9aa3e505ba25Winson Chung LauncherAppState app = LauncherAppState.getInstance(); 239892c74d460ad98c6306420e1127c9aa3e505ba25Winson Chung boolean launcherNotLoaded = (app.getDynamicGrid() == null); 240b7bea81c7a83994c694b6ea643a3edad29cce3f3Winson Chung 241f561bdf68dd304e1d7c3f7796621a4cc39ca15f2Winson Chung PendingInstallShortcutInfo info = new PendingInstallShortcutInfo(data, name, intent); 24248c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka info.icon = icon; 24348c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka info.iconResource = iconResource; 244997a92348a6d6e061737399321393449c16cd4d8Winson Chung 245997a92348a6d6e061737399321393449c16cd4d8Winson Chung String spKey = LauncherAppState.getSharedPreferencesKey(); 246997a92348a6d6e061737399321393449c16cd4d8Winson Chung SharedPreferences sp = context.getSharedPreferences(spKey, Context.MODE_PRIVATE); 247997a92348a6d6e061737399321393449c16cd4d8Winson Chung addToInstallQueue(sp, info); 248997a92348a6d6e061737399321393449c16cd4d8Winson Chung if (!mUseInstallQueue && !launcherNotLoaded) { 249997a92348a6d6e061737399321393449c16cd4d8Winson Chung flushInstallQueue(context); 250f561bdf68dd304e1d7c3f7796621a4cc39ca15f2Winson Chung } 251f561bdf68dd304e1d7c3f7796621a4cc39ca15f2Winson Chung } 252f561bdf68dd304e1d7c3f7796621a4cc39ca15f2Winson Chung 253f561bdf68dd304e1d7c3f7796621a4cc39ca15f2Winson Chung static void enableInstallQueue() { 254f561bdf68dd304e1d7c3f7796621a4cc39ca15f2Winson Chung mUseInstallQueue = true; 255f561bdf68dd304e1d7c3f7796621a4cc39ca15f2Winson Chung } 256f561bdf68dd304e1d7c3f7796621a4cc39ca15f2Winson Chung static void disableAndFlushInstallQueue(Context context) { 257f561bdf68dd304e1d7c3f7796621a4cc39ca15f2Winson Chung mUseInstallQueue = false; 258de0fb8fbf11caac382589ab464b50d528942273aWinson Chung flushInstallQueue(context); 259de0fb8fbf11caac382589ab464b50d528942273aWinson Chung } 260de0fb8fbf11caac382589ab464b50d528942273aWinson Chung static void flushInstallQueue(Context context) { 261cc8befac3550c81d04ea206c170adae36c9a6c93Daniel Sandler String spKey = LauncherAppState.getSharedPreferencesKey(); 26248c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka SharedPreferences sp = context.getSharedPreferences(spKey, Context.MODE_PRIVATE); 26348c7a93eb7ef15ef551aee80c179b8e7e0d2399aMichael Jurka ArrayList<PendingInstallShortcutInfo> installQueue = getAndClearInstallQueue(sp); 264997a92348a6d6e061737399321393449c16cd4d8Winson Chung if (!installQueue.isEmpty()) { 265997a92348a6d6e061737399321393449c16cd4d8Winson Chung Iterator<PendingInstallShortcutInfo> iter = installQueue.iterator(); 266997a92348a6d6e061737399321393449c16cd4d8Winson Chung ArrayList<ItemInfo> addShortcuts = new ArrayList<ItemInfo>(); 267997a92348a6d6e061737399321393449c16cd4d8Winson Chung int result = INSTALL_SHORTCUT_SUCCESSFUL; 268997a92348a6d6e061737399321393449c16cd4d8Winson Chung String duplicateName = ""; 269997a92348a6d6e061737399321393449c16cd4d8Winson Chung while (iter.hasNext()) { 270997a92348a6d6e061737399321393449c16cd4d8Winson Chung final PendingInstallShortcutInfo pendingInfo = iter.next(); 27126acafbe67565a9cc6aeb7d6c43a1c6d1cb9b73cMichael Jurka //final Intent data = pendingInfo.data; 272997a92348a6d6e061737399321393449c16cd4d8Winson Chung final Intent intent = pendingInfo.launchIntent; 273997a92348a6d6e061737399321393449c16cd4d8Winson Chung final String name = pendingInfo.name; 274dff0bfeb901e767ca9b57f9b8051c7fd077626e9Nilesh Agrawal 275dff0bfeb901e767ca9b57f9b8051c7fd077626e9Nilesh Agrawal if (AppsCustomizePagedView.DISABLE_ALL_APPS && !isValidShortcutLaunchIntent(intent)) { 276dff0bfeb901e767ca9b57f9b8051c7fd077626e9Nilesh Agrawal if (DBG) Log.d(TAG, "Ignoring shortcut with launchIntent:" + intent); 277dff0bfeb901e767ca9b57f9b8051c7fd077626e9Nilesh Agrawal continue; 278dff0bfeb901e767ca9b57f9b8051c7fd077626e9Nilesh Agrawal } 279dff0bfeb901e767ca9b57f9b8051c7fd077626e9Nilesh Agrawal 280997a92348a6d6e061737399321393449c16cd4d8Winson Chung final boolean exists = LauncherModel.shortcutExists(context, name, intent); 28126acafbe67565a9cc6aeb7d6c43a1c6d1cb9b73cMichael Jurka //final boolean allowDuplicate = data.getBooleanExtra(Launcher.EXTRA_SHORTCUT_DUPLICATE, true); 282997a92348a6d6e061737399321393449c16cd4d8Winson Chung 283997a92348a6d6e061737399321393449c16cd4d8Winson Chung // TODO-XXX: Disable duplicates for now 284997a92348a6d6e061737399321393449c16cd4d8Winson Chung if (!exists /* && allowDuplicate */) { 285997a92348a6d6e061737399321393449c16cd4d8Winson Chung // Generate a shortcut info to add into the model 286997a92348a6d6e061737399321393449c16cd4d8Winson Chung ShortcutInfo info = getShortcutInfo(context, pendingInfo.data, 287997a92348a6d6e061737399321393449c16cd4d8Winson Chung pendingInfo.launchIntent); 288997a92348a6d6e061737399321393449c16cd4d8Winson Chung addShortcuts.add(info); 289997a92348a6d6e061737399321393449c16cd4d8Winson Chung } 290997a92348a6d6e061737399321393449c16cd4d8Winson Chung /* 291997a92348a6d6e061737399321393449c16cd4d8Winson Chung else if (exists && !allowDuplicate) { 292997a92348a6d6e061737399321393449c16cd4d8Winson Chung result = INSTALL_SHORTCUT_IS_DUPLICATE; 293997a92348a6d6e061737399321393449c16cd4d8Winson Chung duplicateName = name; 294a2413751e3a698aef9c87411a639637883856939Winson Chung } 295997a92348a6d6e061737399321393449c16cd4d8Winson Chung */ 29631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project } 297e428e292c27e650ab4de76b78728ba55019cd282Winson Chung 298997a92348a6d6e061737399321393449c16cd4d8Winson Chung // Notify the user once if we weren't able to place any duplicates 299997a92348a6d6e061737399321393449c16cd4d8Winson Chung if (result == INSTALL_SHORTCUT_IS_DUPLICATE) { 300997a92348a6d6e061737399321393449c16cd4d8Winson Chung Toast.makeText(context, context.getString(R.string.shortcut_duplicate, 301997a92348a6d6e061737399321393449c16cd4d8Winson Chung duplicateName), Toast.LENGTH_SHORT).show(); 302f0c6ae0e35d3e020db55b5b826955da66b14b7f6Winson Chung } 303f0c6ae0e35d3e020db55b5b826955da66b14b7f6Winson Chung 304997a92348a6d6e061737399321393449c16cd4d8Winson Chung // Add the new apps to the model and bind them 305997a92348a6d6e061737399321393449c16cd4d8Winson Chung if (!addShortcuts.isEmpty()) { 306997a92348a6d6e061737399321393449c16cd4d8Winson Chung LauncherAppState app = LauncherAppState.getInstance(); 307fe9d96a39651de99819208e95c7ec7d432a402e0Winson Chung app.getModel().addAndBindAddedApps(context, addShortcuts, new ArrayList<AppInfo>()); 30831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project } 30931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project } 31031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project } 31131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project 312dff0bfeb901e767ca9b57f9b8051c7fd077626e9Nilesh Agrawal /** 313dff0bfeb901e767ca9b57f9b8051c7fd077626e9Nilesh Agrawal * Returns true if the intent is a valid launch intent for a shortcut. 314dff0bfeb901e767ca9b57f9b8051c7fd077626e9Nilesh Agrawal * This is used to identify shortcuts which are different from the ones exposed by the 315dff0bfeb901e767ca9b57f9b8051c7fd077626e9Nilesh Agrawal * applications' manifest file. 316dff0bfeb901e767ca9b57f9b8051c7fd077626e9Nilesh Agrawal * 317dff0bfeb901e767ca9b57f9b8051c7fd077626e9Nilesh Agrawal * When DISABLE_ALL_APPS is true, shortcuts exposed via the app's manifest should never be 318dff0bfeb901e767ca9b57f9b8051c7fd077626e9Nilesh Agrawal * duplicated or removed(unless the app is un-installed). 319dff0bfeb901e767ca9b57f9b8051c7fd077626e9Nilesh Agrawal * 320dff0bfeb901e767ca9b57f9b8051c7fd077626e9Nilesh Agrawal * @param launchIntent The intent that will be launched when the shortcut is clicked. 321dff0bfeb901e767ca9b57f9b8051c7fd077626e9Nilesh Agrawal */ 322dff0bfeb901e767ca9b57f9b8051c7fd077626e9Nilesh Agrawal static boolean isValidShortcutLaunchIntent(Intent launchIntent) { 323dff0bfeb901e767ca9b57f9b8051c7fd077626e9Nilesh Agrawal if (launchIntent != null 324dff0bfeb901e767ca9b57f9b8051c7fd077626e9Nilesh Agrawal && Intent.ACTION_MAIN.equals(launchIntent.getAction()) 325dff0bfeb901e767ca9b57f9b8051c7fd077626e9Nilesh Agrawal && launchIntent.getComponent() != null 326dff0bfeb901e767ca9b57f9b8051c7fd077626e9Nilesh Agrawal && launchIntent.getCategories() != null 327dff0bfeb901e767ca9b57f9b8051c7fd077626e9Nilesh Agrawal && launchIntent.getCategories().size() == 1 328dff0bfeb901e767ca9b57f9b8051c7fd077626e9Nilesh Agrawal && launchIntent.hasCategory(Intent.CATEGORY_LAUNCHER) 329dff0bfeb901e767ca9b57f9b8051c7fd077626e9Nilesh Agrawal && launchIntent.getExtras() == null 330dff0bfeb901e767ca9b57f9b8051c7fd077626e9Nilesh Agrawal && TextUtils.isEmpty(launchIntent.getDataString())) { 331dff0bfeb901e767ca9b57f9b8051c7fd077626e9Nilesh Agrawal return false; 332dff0bfeb901e767ca9b57f9b8051c7fd077626e9Nilesh Agrawal } 333dff0bfeb901e767ca9b57f9b8051c7fd077626e9Nilesh Agrawal return true; 334dff0bfeb901e767ca9b57f9b8051c7fd077626e9Nilesh Agrawal } 335dff0bfeb901e767ca9b57f9b8051c7fd077626e9Nilesh Agrawal 336997a92348a6d6e061737399321393449c16cd4d8Winson Chung private static ShortcutInfo getShortcutInfo(Context context, Intent data, 337997a92348a6d6e061737399321393449c16cd4d8Winson Chung Intent launchIntent) { 338997a92348a6d6e061737399321393449c16cd4d8Winson Chung if (launchIntent.getAction() == null) { 339997a92348a6d6e061737399321393449c16cd4d8Winson Chung launchIntent.setAction(Intent.ACTION_VIEW); 340997a92348a6d6e061737399321393449c16cd4d8Winson Chung } else if (launchIntent.getAction().equals(Intent.ACTION_MAIN) && 341997a92348a6d6e061737399321393449c16cd4d8Winson Chung launchIntent.getCategories() != null && 342997a92348a6d6e061737399321393449c16cd4d8Winson Chung launchIntent.getCategories().contains(Intent.CATEGORY_LAUNCHER)) { 343997a92348a6d6e061737399321393449c16cd4d8Winson Chung launchIntent.addFlags( 344997a92348a6d6e061737399321393449c16cd4d8Winson Chung Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); 34531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project } 346997a92348a6d6e061737399321393449c16cd4d8Winson Chung LauncherAppState app = LauncherAppState.getInstance(); 3472c1afde5cd60eb13f05aeae0c170e697493ef307Winson Chung ShortcutInfo info = app.getModel().infoFromShortcutIntent(context, data, null); 3482c1afde5cd60eb13f05aeae0c170e697493ef307Winson Chung info.title = ensureValidName(context, launchIntent, info.title); 3492c1afde5cd60eb13f05aeae0c170e697493ef307Winson Chung return info; 3502c1afde5cd60eb13f05aeae0c170e697493ef307Winson Chung } 3512c1afde5cd60eb13f05aeae0c170e697493ef307Winson Chung 3522c1afde5cd60eb13f05aeae0c170e697493ef307Winson Chung /** 3532c1afde5cd60eb13f05aeae0c170e697493ef307Winson Chung * Ensures that we have a valid, non-null name. If the provided name is null, we will return 3542c1afde5cd60eb13f05aeae0c170e697493ef307Winson Chung * the application name instead. 3552c1afde5cd60eb13f05aeae0c170e697493ef307Winson Chung */ 3562c1afde5cd60eb13f05aeae0c170e697493ef307Winson Chung private static CharSequence ensureValidName(Context context, Intent intent, CharSequence name) { 3572c1afde5cd60eb13f05aeae0c170e697493ef307Winson Chung if (name == null) { 3582c1afde5cd60eb13f05aeae0c170e697493ef307Winson Chung try { 3592c1afde5cd60eb13f05aeae0c170e697493ef307Winson Chung PackageManager pm = context.getPackageManager(); 3602c1afde5cd60eb13f05aeae0c170e697493ef307Winson Chung ActivityInfo info = pm.getActivityInfo(intent.getComponent(), 0); 3612c1afde5cd60eb13f05aeae0c170e697493ef307Winson Chung name = info.loadLabel(pm).toString(); 3622c1afde5cd60eb13f05aeae0c170e697493ef307Winson Chung } catch (PackageManager.NameNotFoundException nnfe) { 3632c1afde5cd60eb13f05aeae0c170e697493ef307Winson Chung return ""; 3642c1afde5cd60eb13f05aeae0c170e697493ef307Winson Chung } 3652c1afde5cd60eb13f05aeae0c170e697493ef307Winson Chung } 3662c1afde5cd60eb13f05aeae0c170e697493ef307Winson Chung return name; 36731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project } 36831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project} 369