Persist list of shortcuts to install on home workspace

Bug: 8707110

Change-Id: I6740350040dceb0edaf4295d6a5af62a088a3055
diff --git a/src/com/android/launcher2/InstallShortcutReceiver.java b/src/com/android/launcher2/InstallShortcutReceiver.java
index 337097e..2e86a7f 100644
--- a/src/com/android/launcher2/InstallShortcutReceiver.java
+++ b/src/com/android/launcher2/InstallShortcutReceiver.java
@@ -22,6 +22,10 @@
 import android.content.SharedPreferences;
 import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManager;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.util.Base64;
+import android.util.Log;
 import android.widget.Toast;
 
 import com.android.launcher.R;
@@ -31,12 +35,23 @@
 import java.util.Iterator;
 import java.util.Set;
 
+import org.json.*;
+
 public class InstallShortcutReceiver extends BroadcastReceiver {
     public static final String ACTION_INSTALL_SHORTCUT =
             "com.android.launcher.action.INSTALL_SHORTCUT";
     public static final String NEW_APPS_PAGE_KEY = "apps.new.page";
     public static final String NEW_APPS_LIST_KEY = "apps.new.list";
 
+    public static final String DATA_INTENT_KEY = "intent.data";
+    public static final String LAUNCH_INTENT_KEY = "intent.launch";
+    public static final String NAME_KEY = "name";
+    public static final String ICON_KEY = "icon";
+    public static final String ICON_RESOURCE_NAME_KEY = "iconResource";
+    public static final String ICON_RESOURCE_PACKAGE_NAME_KEY = "iconResourcePackage";
+    // The set of shortcuts that are pending install
+    public static final String APPS_PENDING_INSTALL = "apps_to_install";
+
     public static final int NEW_SHORTCUT_BOUNCE_DURATION = 450;
     public static final int NEW_SHORTCUT_STAGGER_DELAY = 75;
 
@@ -50,10 +65,6 @@
 
     private static Object sLock = new Object();
 
-    // The set of shortcuts that are pending install
-    private static ArrayList<PendingInstallShortcutInfo> mInstallQueue =
-            new ArrayList<PendingInstallShortcutInfo>();
-
     private static void addToStringSet(SharedPreferences sharedPrefs,
             SharedPreferences.Editor editor, String key, String value) {
         Set<String> strings = sharedPrefs.getStringSet(key, null);
@@ -65,6 +76,82 @@
         strings.add(value);
         editor.putStringSet(key, strings);
     }
+
+    private static void addToInstallQueue(
+            SharedPreferences sharedPrefs, PendingInstallShortcutInfo info) {
+        synchronized(sLock) {
+            try {
+                JSONStringer json = new JSONStringer()
+                    .object()
+                    .key(DATA_INTENT_KEY).value(info.data.toUri(0))
+                    .key(LAUNCH_INTENT_KEY).value(info.launchIntent.toUri(0))
+                    .key(NAME_KEY).value(info.name);
+                if (info.icon != null) {
+                    byte[] iconByteArray = ItemInfo.flattenBitmap(info.icon);
+                    json = json.key(ICON_KEY).value(
+                        Base64.encodeToString(
+                            iconByteArray, 0, iconByteArray.length, Base64.DEFAULT));
+                }
+                if (info.iconResource != null) {
+                    json = json.key(ICON_RESOURCE_NAME_KEY).value(info.iconResource.resourceName);
+                    json = json.key(ICON_RESOURCE_PACKAGE_NAME_KEY)
+                        .value(info.iconResource.packageName);
+                }
+                json = json.endObject();
+                SharedPreferences.Editor editor = sharedPrefs.edit();
+                addToStringSet(sharedPrefs, editor, APPS_PENDING_INSTALL, json.toString());
+                editor.commit();
+            } catch (org.json.JSONException e) {
+                Log.d("InstallShortcutReceiver", "Exception when adding shortcut: " + e);
+            }
+        }
+    }
+
+    private static ArrayList<PendingInstallShortcutInfo> getAndClearInstallQueue(
+            SharedPreferences sharedPrefs) {
+        synchronized(sLock) {
+            Set<String> strings = sharedPrefs.getStringSet(APPS_PENDING_INSTALL, null);
+            if (strings == null) {
+                return new ArrayList<PendingInstallShortcutInfo>();
+            }
+            ArrayList<PendingInstallShortcutInfo> infos =
+                new ArrayList<PendingInstallShortcutInfo>();
+            for (String json : strings) {
+                try {
+                    JSONObject object = (JSONObject) new JSONTokener(json).nextValue();
+                    Intent data = Intent.parseUri(object.getString(DATA_INTENT_KEY), 0);
+                    Intent launchIntent = Intent.parseUri(object.getString(LAUNCH_INTENT_KEY), 0);
+                    String name = object.getString(NAME_KEY);
+                    String iconBase64 = object.optString(ICON_KEY);
+                    String iconResourceName = object.optString(ICON_RESOURCE_NAME_KEY);
+                    String iconResourcePackageName =
+                        object.optString(ICON_RESOURCE_PACKAGE_NAME_KEY);
+                    if (iconBase64 != null && !iconBase64.isEmpty()) {
+                        byte[] iconArray = Base64.decode(iconBase64, Base64.DEFAULT);
+                        Bitmap b = BitmapFactory.decodeByteArray(iconArray, 0, iconArray.length);
+                        data.putExtra(Intent.EXTRA_SHORTCUT_ICON, b);
+                    } else if (iconResourceName != null && !iconResourceName.isEmpty()) {
+                        Intent.ShortcutIconResource iconResource =
+                            new Intent.ShortcutIconResource();
+                        iconResource.resourceName = iconResourceName;
+                        iconResource.packageName = iconResourcePackageName;
+                        data.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, iconResource);
+                    }
+                    data.putExtra(Intent.EXTRA_SHORTCUT_INTENT, launchIntent);
+                    PendingInstallShortcutInfo info =
+                        new PendingInstallShortcutInfo(data, name, launchIntent);
+                    infos.add(info);
+                } catch (org.json.JSONException e) {
+                    Log.d("InstallShortcutReceiver", "Exception reading shortcut to add: " + e);
+                } catch (java.net.URISyntaxException e) {
+                    Log.d("InstallShortcutReceiver", "Exception reading shortcut to add: " + e);
+                }
+            }
+            sharedPrefs.edit().putStringSet(APPS_PENDING_INSTALL, new HashSet<String>()).commit();
+            return infos;
+        }
+    }
+
     // Determines whether to defer installing shortcuts immediately until
     // processAllPendingInstalls() is called.
     private static boolean mUseInstallQueue = false;
@@ -73,6 +160,8 @@
         Intent data;
         Intent launchIntent;
         String name;
+        Bitmap icon;
+        Intent.ShortcutIconResource iconResource;
 
         public PendingInstallShortcutInfo(Intent rawData, String shortcutName,
                 Intent shortcutIntent) {
@@ -103,13 +192,21 @@
                 return;
             }
         }
+        Bitmap icon = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON);
+        Intent.ShortcutIconResource iconResource =
+            data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE);
+
         // Queue the item up for adding if launcher has not loaded properly yet
         boolean launcherNotLoaded = LauncherModel.getCellCountX() <= 0 ||
                 LauncherModel.getCellCountY() <= 0;
 
         PendingInstallShortcutInfo info = new PendingInstallShortcutInfo(data, name, intent);
+        info.icon = icon;
+        info.iconResource = iconResource;
         if (mUseInstallQueue || launcherNotLoaded) {
-            mInstallQueue.add(info);
+            String spKey = LauncherApplication.getSharedPreferencesKey();
+            SharedPreferences sp = context.getSharedPreferences(spKey, Context.MODE_PRIVATE);
+            addToInstallQueue(sp, info);
         } else {
             processInstallShortcut(context, info);
         }
@@ -123,10 +220,12 @@
         flushInstallQueue(context);
     }
     static void flushInstallQueue(Context context) {
-        Iterator<PendingInstallShortcutInfo> iter = mInstallQueue.iterator();
+        String spKey = LauncherApplication.getSharedPreferencesKey();
+        SharedPreferences sp = context.getSharedPreferences(spKey, Context.MODE_PRIVATE);
+        ArrayList<PendingInstallShortcutInfo> installQueue = getAndClearInstallQueue(sp);
+        Iterator<PendingInstallShortcutInfo> iter = installQueue.iterator();
         while (iter.hasNext()) {
             processInstallShortcut(context, iter.next());
-            iter.remove();
         }
     }