Logging to disk for more reliable logging

-> Re-adding basic distinction btw apps and shortcuts
-> Adding functionality to e-mail dump file, but not using

Change-Id: I2b635004ab082af79445158ab61f20aac62d2e2f
diff --git a/src/com/android/launcher3/DragController.java b/src/com/android/launcher3/DragController.java
index d3973f6..07b2528 100644
--- a/src/com/android/launcher3/DragController.java
+++ b/src/com/android/launcher3/DragController.java
@@ -687,6 +687,10 @@
             }
         }
         mDragObject.dragSource.onDropCompleted((View) dropTarget, mDragObject, false, accepted);
+
+        // Write all the logs to disk
+        Launcher.addDumpLog(TAG, "10249126 - DragController.drop() - dumping logs to disk", true);
+        mLauncher.dumpLogsToLocalData(false);
     }
 
     private DropTarget findDropTarget(int x, int y, int[] dropCoordinates) {
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index dd9e793..35e166f 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -101,12 +101,21 @@
 
 import java.io.DataInputStream;
 import java.io.DataOutputStream;
+import java.io.File;
 import java.io.FileDescriptor;
+import java.io.FileInputStream;
 import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
 import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
 import java.io.PrintWriter;
+import java.text.DateFormat;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Date;
 import java.util.HashMap;
 import java.util.List;
 
@@ -302,6 +311,11 @@
     private final ArrayList<Integer> mSynchronouslyBoundPages = new ArrayList<Integer>();
 
     static final ArrayList<String> sDumpLogs = new ArrayList<String>();
+    static Date sDateStamp = new Date();
+    static DateFormat sDateFormat =
+            DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT);
+    static long sRunStart = System.currentTimeMillis();
+    static final String CORRUPTION_EMAIL_SENT_KEY = "corruptionEmailSent";
 
     // We only want to get the SharedPreferences once since it does an FS stat each time we get
     // it from the context.
@@ -853,6 +867,10 @@
         if (DEBUG_RESUME_TIME) {
             Log.d(TAG, "Time spent in onResume: " + (System.currentTimeMillis() - startTime));
         }
+
+        // Write all the logs to disk
+        Launcher.addDumpLog(TAG, "10249126 - onResume() - dumping logs to disk", true);
+        dumpLogsToLocalData(false);
     }
 
     @Override
@@ -869,6 +887,10 @@
         mPaused = true;
         mDragController.cancelDrag();
         mDragController.resetLastGestureUpTime();
+
+        // Write all the logs to disk
+        Launcher.addDumpLog(TAG, "10249126 - onPause() - dumping logs to disk", true);
+        dumpLogsToLocalData(false);
     }
 
     protected void onFinishBindingItems() {
@@ -3527,7 +3549,7 @@
     public void bindAddScreens(ArrayList<Long> orderedScreenIds) {
         int count = orderedScreenIds.size();
         for (int i = 0; i < count; i++) {
-            Log.w(TAG, "10249126 - bindAddScreens(" + orderedScreenIds.get(i) + ")");
+            Launcher.addDumpLog(TAG, "10249126 - bindAddScreens(" + orderedScreenIds.get(i) + ")", true);
             mWorkspace.insertNewWorkspaceScreenBeforeEmptyScreen(orderedScreenIds.get(i));
         }
     }
@@ -3540,6 +3562,22 @@
         return show;
     }
 
+    private boolean emailSent() {
+        String spKey = LauncherAppState.getSharedPreferencesKey();
+        SharedPreferences sp = getSharedPreferences(spKey, Context.MODE_PRIVATE);
+        boolean show = sp.getBoolean(CORRUPTION_EMAIL_SENT_KEY, false);
+        return show;
+    }
+
+    private void setEmailSent(boolean sent) {
+        String spKey = LauncherAppState.getSharedPreferencesKey();
+        SharedPreferences sp = getSharedPreferences(spKey, Context.MODE_PRIVATE);
+
+        SharedPreferences.Editor editor = sp.edit();
+        editor.putBoolean(CORRUPTION_EMAIL_SENT_KEY, sent);
+        editor.commit();
+    }
+
     private void toggleShowWeightWatcher() {
         String spKey = LauncherAppState.getSharedPreferencesKey();
         SharedPreferences sp = getSharedPreferences(spKey, Context.MODE_PRIVATE);
@@ -3568,7 +3606,7 @@
             return;
         }
 
-        Log.w(TAG, "10249126 - bindAppsAdded(" + newScreens.size() + ")");
+        Launcher.addDumpLog(TAG, "10249126 - bindAppsAdded(" + newScreens.size() + ")", true);
 
         // Add the new screens
         bindAddScreens(newScreens);
@@ -3777,8 +3815,8 @@
 
         mWorkspaceLoading = false;
         if (upgradePath) {
-            mWorkspace.stripDuplicateApps();
-            mIntentsOnWorkspaceFromUpgradePath = mWorkspace.stripDuplicateApps();
+            mWorkspace.getUniqueComponents(true, null);
+            mIntentsOnWorkspaceFromUpgradePath = mWorkspace.getUniqueComponents(true, null);
         }
 
         mWorkspace.post(new Runnable() {
@@ -3787,6 +3825,10 @@
                 onFinishBindingItems();
             }
         });
+
+        // Write all the logs to disk
+        Launcher.addDumpLog(TAG, "10249126 - finishBindingItems() - dumping logs to disk", true);
+        dumpLogsToLocalData(false);
     }
 
     private boolean canRunNewAppsAnimation() {
@@ -4154,22 +4196,131 @@
     @Override
     public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
         super.dump(prefix, fd, writer, args);
-        writer.println(" ");
-        writer.println("Debug logs: ");
-        for (int i = 0; i < sDumpLogs.size(); i++) {
-            writer.println("  " + sDumpLogs.get(i));
+        synchronized (sDumpLogs) {
+            writer.println(" ");
+            writer.println("Debug logs: ");
+            for (int i = 0; i < sDumpLogs.size(); i++) {
+                writer.println("  " + sDumpLogs.get(i));
+            }
         }
     }
 
     public static void dumpDebugLogsToConsole() {
-        Log.d(TAG, "");
-        Log.d(TAG, "*********************");
-        Log.d(TAG, "Launcher debug logs: ");
-        for (int i = 0; i < sDumpLogs.size(); i++) {
-            Log.d(TAG, "  " + sDumpLogs.get(i));
+        synchronized (sDumpLogs) {
+            Log.d(TAG, "");
+            Log.d(TAG, "*********************");
+            Log.d(TAG, "Launcher debug logs: ");
+            for (int i = 0; i < sDumpLogs.size(); i++) {
+                Log.d(TAG, "  " + sDumpLogs.get(i));
+            }
+            Log.d(TAG, "*********************");
+            Log.d(TAG, "");
         }
-        Log.d(TAG, "*********************");
-        Log.d(TAG, "");
+    }
+
+    public static void addDumpLog(String tag, String log, boolean debugLog) {
+        if (debugLog) {
+            Log.d(tag, log);
+        }
+        sDateStamp.setTime(System.currentTimeMillis());
+        synchronized (sDumpLogs) {
+            sDumpLogs.add(sDateFormat.format(sDateStamp) + ": " + tag + ", " + log);
+        }
+    }
+
+    public void dumpLogsToLocalData(final boolean email) {
+        new Thread("DumpLogsToLocalData") {
+            @Override
+            public void run() {
+                boolean success = false;
+                sDateStamp.setTime(sRunStart);
+                String FILENAME = sDateStamp.getMonth() + "-"
+                        + sDateStamp.getDay() + "_"
+                        + sDateStamp.getHours() + "-"
+                        + sDateStamp.getMinutes() + "_"
+                        + sDateStamp.getSeconds() + ".txt";
+
+                FileOutputStream fos = null;
+                File outFile = null;
+                try {
+                    outFile = new File(getFilesDir(), FILENAME);
+                    outFile.createNewFile();
+                    fos = new FileOutputStream(outFile);
+                } catch (Exception e) {
+                    e.printStackTrace();
+                }
+                if (fos != null) {
+                    PrintWriter writer = new PrintWriter(fos);
+
+                    writer.println(" ");
+                    writer.println("Debug logs: ");
+                    synchronized (sDumpLogs) {
+                        for (int i = 0; i < sDumpLogs.size(); i++) {
+                            writer.println("  " + sDumpLogs.get(i));
+                        }
+                    }
+                    writer.close();
+                }
+                try {
+                    if (fos != null) {
+                        fos.close();
+                        success = true;
+                    }
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+
+                if (success && email) {
+                    if (!emailSent()) {
+                        emailFile(outFile);
+                    }
+                }
+            }
+        }.start();
+    }
+
+    private void emailFile(File file) {
+        File publicCopy = new File(Environment.getExternalStorageDirectory(), file.getName());
+        try {
+            copyFile(file, publicCopy);
+        } catch (IOException e) {
+            e.printStackTrace();
+            return;
+        }
+
+        Intent intent = new Intent(Intent.ACTION_SEND);
+        intent.setType("text/plain");
+        intent.putExtra(Intent.EXTRA_EMAIL, new String[] {"adamcohen@google.com, winsonc@google.com," +
+			"mikejurka@google"});
+        intent.putExtra(Intent.EXTRA_SUBJECT, "Data corruption " + file.getName());
+        intent.putExtra(Intent.EXTRA_TEXT, "Data corruption has occurred, logs attached");
+
+        if (!file.exists() || !file.canRead()) {
+            Toast.makeText(this, "Attachment Error", Toast.LENGTH_SHORT).show();
+            finish();
+            return;
+        }
+
+        Toast.makeText(this, "Data corruption has occurred, please send e-mail", Toast.LENGTH_LONG);
+        Uri uri = Uri.fromFile(publicCopy);
+        intent.putExtra(Intent.EXTRA_STREAM, uri);
+        startActivity(Intent.createChooser(intent, "Please send logs, consider clearing data"));
+
+        setEmailSent(true);
+    }
+
+    public void copyFile(File src, File dst) throws IOException {
+        InputStream in = new FileInputStream(src);
+        OutputStream out = new FileOutputStream(dst);
+
+        // Transfer bytes from in to out
+        byte[] buf = new byte[1024];
+        int len;
+        while ((len = in.read(buf)) > 0) {
+            out.write(buf, 0, len);
+        }
+        in.close();
+        out.close();
     }
 }
 
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index c76b553..1c60861 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -108,7 +108,7 @@
     private WeakReference<Callbacks> mCallbacks;
 
     // < only access in worker thread >
-    private AllAppsList mBgAllAppsList;
+    AllAppsList mBgAllAppsList;
 
     // The lock that must be acquired before referencing any static bg data structures.  Unlike
     // other locks, this one can generally be held long-term because we never expect any of these
@@ -167,6 +167,7 @@
         public void bindPackagesUpdated(ArrayList<Object> widgetsAndShortcuts);
         public void bindSearchablesChanged();
         public void onPageBoundSynchronously(int page);
+        public void dumpLogsToLocalData(boolean email);
     }
 
     public interface ItemInfoFilter {
@@ -281,7 +282,7 @@
     }
     public void addAndBindAddedApps(final Context context, final ArrayList<ItemInfo> added,
                                     final Callbacks callbacks) {
-        Log.w(TAG, "10249126 - addAndBindAddedApps()");
+        Launcher.addDumpLog(TAG, "10249126 - addAndBindAddedApps()", true);
         if (added.isEmpty()) {
             return;
         }
@@ -328,7 +329,7 @@
                                     workspaceScreens.size());
                             while (numPagesToAdd > 0) {
                                 long screenId = lp.generateNewScreenId();
-                                Log.w(TAG, "10249126 - addAndBindAddedApps(" + screenId + ")");
+                                Launcher.addDumpLog(TAG, "10249126 - addAndBindAddedApps(" + screenId + ")", true);
                                 // Save the screen id for binding in the workspace
                                 workspaceScreens.add(screenId);
                                 addedWorkspaceScreensFinal.add(screenId);
@@ -360,7 +361,7 @@
                     }
                 }
 
-                Log.w(TAG, "10249126 - addAndBindAddedApps - updateWorkspaceScreenOrder(" + workspaceScreens.size() + ")");
+                Launcher.addDumpLog(TAG, "10249126 - addAndBindAddedApps - updateWorkspaceScreenOrder(" + workspaceScreens.size() + ")", true);
 
                 // Update the workspace screens
                 updateWorkspaceScreenOrder(context, workspaceScreens);
@@ -630,8 +631,8 @@
         String transaction = "DbDebug    Modify item (" + item.title + ") in db, id: " + item.id +
                 " (" + item.container + ", " + item.screenId + ", " + item.cellX + ", " + item.cellY +
                 ") --> " + "(" + container + ", " + screenId + ", " + cellX + ", " + cellY + ")";
-        Launcher.sDumpLogs.add(transaction);
-        Log.d(TAG, transaction);
+        Launcher.addDumpLog(TAG, transaction, true);
+
         item.container = container;
         item.cellX = cellX;
         item.cellY = cellY;
@@ -670,7 +671,7 @@
                     + item.id + " (" + item.container + ", " + item.screenId + ", " + item.cellX
                     + ", " + item.cellY + ") --> " + "(" + container + ", " + screen + ", "
                     + item.cellX + ", " + item.cellY + ")";
-            Launcher.sDumpLogs.add(transaction);
+            Launcher.addDumpLog(TAG, transaction, true);
             item.container = container;
 
             // We store hotseat items in canonical form which is this orientation invariant position
@@ -702,8 +703,8 @@
         String transaction = "DbDebug    Modify item (" + item.title + ") in db, id: " + item.id +
                 " (" + item.container + ", " + item.screenId + ", " + item.cellX + ", " + item.cellY +
                 ") --> " + "(" + container + ", " + screenId + ", " + cellX + ", " + cellY + ")";
-        Launcher.sDumpLogs.add(transaction);
-        Log.d(TAG, transaction);
+        Launcher.addDumpLog(TAG, transaction, true);
+
         item.cellX = cellX;
         item.cellY = cellY;
         item.spanX = spanX;
@@ -872,8 +873,7 @@
                 String transaction = "DbDebug    Add item (" + item.title + ") to db, id: "
                         + item.id + " (" + container + ", " + screenId + ", " + cellX + ", "
                         + cellY + ")";
-                Launcher.sDumpLogs.add(transaction);
-                Log.d(TAG, transaction);
+                Launcher.addDumpLog(TAG, transaction, true);
 
                 cr.insert(notify ? LauncherSettings.Favorites.CONTENT_URI :
                         LauncherSettings.Favorites.CONTENT_URI_NO_NOTIFICATION, values);
@@ -934,8 +934,7 @@
                 String transaction = "DbDebug    Delete item (" + item.title + ") from db, id: "
                         + item.id + " (" + item.container + ", " + item.screenId + ", " + item.cellX +
                         ", " + item.cellY + ")";
-                Launcher.sDumpLogs.add(transaction);
-                Log.d(TAG, transaction);
+                Launcher.addDumpLog(TAG, transaction, true);
 
                 cr.delete(uriToDelete, null, null);
 
@@ -977,7 +976,7 @@
      * a list of screen ids in the order that they should appear.
      */
     void updateWorkspaceScreenOrder(Context context, final ArrayList<Long> screens) {
-        Log.w(TAG, "10249126 - updateWorkspaceScreenOrder()");
+        Launcher.addDumpLog(TAG, "10249126 - updateWorkspaceScreenOrder()", true);
         final ArrayList<Long> screensCopy = new ArrayList<Long>(screens);
         final ContentResolver cr = context.getContentResolver();
         final Uri uri = LauncherSettings.WorkspaceScreens.CONTENT_URI;
@@ -987,15 +986,15 @@
         while (iter.hasNext()) {
             long id = iter.next();
             if (id < 0) {
-                Log.w(TAG, "10249126 - updateWorkspaceScreenOrder - remove: " + id + ")");
+                Launcher.addDumpLog(TAG, "10249126 - updateWorkspaceScreenOrder - remove: " + id + ")", true);
                 iter.remove();
             }
         }
 
         // Dump the screens copy
-        Log.w(TAG, "10249126 - updateWorkspaceScreenOrder - screensCopy");
+        Launcher.addDumpLog(TAG, "10249126 - updateWorkspaceScreenOrder - screensCopy", true);
         for (Long l : screensCopy) {
-            Log.w(TAG, "10249126\t- " + l);
+            Launcher.addDumpLog(TAG, "10249126\t- " + l, true);
         }
 
         Runnable r = new Runnable() {
@@ -1010,20 +1009,25 @@
                     long screenId = screensCopy.get(i);
                     v.put(LauncherSettings.WorkspaceScreens._ID, screenId);
                     v.put(LauncherSettings.WorkspaceScreens.SCREEN_RANK, i);
-                    Log.w(TAG, "10249126 - updateWorkspaceScreenOrder - add: " +
-                            screenId + ", " + i + ")");
+                    Launcher.addDumpLog(TAG, "10249126 - updateWorkspaceScreenOrder(" + screenId + ", " + i + ")", true);
                     values[i] = v;
                 }
                 cr.bulkInsert(uri, values);
 
                 // Dump the sBgWorkspaceScreens
-                Log.w(TAG, "10249126 - updateWorkspaceScreenOrder - sBgWorkspaceScreens");
+                Launcher.addDumpLog(TAG, "10249126 - updateWorkspaceScreenOrder - sBgWorkspaceScreens", true);
                 for (Long l : sBgWorkspaceScreens) {
-                    Log.w(TAG, "10249126\t- " + l);
+                    Launcher.addDumpLog(TAG, "10249126\t- " + l, true);
                 }
 
                 sBgWorkspaceScreens.clear();
                 sBgWorkspaceScreens.addAll(screensCopy);
+
+                // Dump the sBgWorkspaceScreens
+                Launcher.addDumpLog(TAG, "10249126 - updateWorkspaceScreenOrder - sBgWorkspaceScreens", true);
+                for (Long l : sBgWorkspaceScreens) {
+                    Launcher.addDumpLog(TAG, "10249126\t- " + l, true);
+                }
             }
         };
         runOnWorkerThread(r);
@@ -1264,7 +1268,7 @@
                     long screenId = sc.getLong(idIndex);
                     int rank = sc.getInt(rankIndex);
 
-                    Log.w(TAG, "10249126 - loadWorkspaceScreensDb(" + screenId + ", " + rank + ")");
+                    Launcher.addDumpLog(TAG, "10249126 - loadWorkspaceScreensDb(" + screenId + ", " + rank + ")", true);
 
                     orderedScreens.put(rank, screenId);
                 } catch (Exception e) {
@@ -1472,9 +1476,10 @@
 
             // Ensure that all the applications that are in the system are represented on the home
             // screen.
-            Log.w(TAG, "10249126 - verifyApplications - useMoreApps="
-                    + UPGRADE_USE_MORE_APPS_FOLDER + " isUpgrade=" + isUpgrade);
+            Launcher.addDumpLog(TAG, "10249126 - verifyApplications - useMoreApps="
+                    + UPGRADE_USE_MORE_APPS_FOLDER + " isUpgrade=" + isUpgrade, true);
             if (!UPGRADE_USE_MORE_APPS_FOLDER || !isUpgrade) {
+                Launcher.addDumpLog(TAG, "10249126 - verifyApplications(" + isUpgrade + ")", true);
                 verifyApplications();
             }
 
@@ -1537,7 +1542,7 @@
             synchronized (sBgLock) {
                 for (ApplicationInfo app : mBgAllAppsList.data) {
                     tmpInfos = getItemInfoForComponentName(app.componentName);
-                    Log.w(TAG, "10249126 - \t" + app.componentName.getPackageName() + ", " + tmpInfos.isEmpty());
+                    Launcher.addDumpLog(TAG, "10249126 - \t" + app.componentName.getPackageName() + ", " + tmpInfos.isEmpty(), true);
                     if (tmpInfos.isEmpty()) {
                         // We are missing an application icon, so add this to the workspace
                         added.add(app);
@@ -1645,7 +1650,7 @@
                 sBgItemsIdMap.clear();
                 sBgDbIconCache.clear();
                 sBgWorkspaceScreens.clear();
-                Log.w(TAG, "10249126 - loadWorkspace()");
+                Launcher.addDumpLog(TAG, "10249126 - loadWorkspace()", true);
 
                 final ArrayList<Long> itemsToRemove = new ArrayList<Long>();
                 final Uri contentUri = LauncherSettings.Favorites.CONTENT_URI;
@@ -1843,7 +1848,7 @@
                                     String log = "Deleting widget that isn't installed anymore: id="
                                         + id + " appWidgetId=" + appWidgetId;
                                     Log.e(TAG, log);
-                                    Launcher.sDumpLogs.add(log);
+                                    Launcher.addDumpLog(TAG, log, false);
                                     itemsToRemove.add(id);
                                 } else {
                                     appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId,
@@ -1895,7 +1900,7 @@
 
                 if (itemsToRemove.size() > 0) {
                     ContentProviderClient client = contentResolver.acquireContentProviderClient(
-                                    LauncherSettings.Favorites.CONTENT_URI);
+                            LauncherSettings.Favorites.CONTENT_URI);
                     // Remove dead items
                     for (long id : itemsToRemove) {
                         if (DEBUG_LOADERS) {
@@ -1912,14 +1917,14 @@
                 }
 
                 if (loadedOldDb) {
-                    Log.w(TAG, "10249126 - loadWorkspace - loadedOldDb");
+                    Launcher.addDumpLog(TAG, "10249126 - loadWorkspace - loadedOldDb", true);
                     long maxScreenId = 0;
                     // If we're importing we use the old screen order.
                     for (ItemInfo item: sBgItemsIdMap.values()) {
                         long screenId = item.screenId;
                         if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP &&
                                 !sBgWorkspaceScreens.contains(screenId)) {
-                            Log.w(TAG, "10249126 - loadWorkspace-loadedOldDb(" + screenId + ")");
+                            Launcher.addDumpLog(TAG, "10249126 - loadWorkspace-loadedOldDb(" + screenId + ")", true);
                             sBgWorkspaceScreens.add(screenId);
                             if (screenId > maxScreenId) {
                                 maxScreenId = screenId;
@@ -1929,9 +1934,9 @@
                     Collections.sort(sBgWorkspaceScreens);
 
                     // Dump the sBgWorkspaceScreens
-                    Log.w(TAG, "10249126 - updateWorkspaceScreenOrder - sBgWorkspaceScreens");
+                    Launcher.addDumpLog(TAG, "10249126 - updateWorkspaceScreenOrder - sBgWorkspaceScreens", true);
                     for (Long l : sBgWorkspaceScreens) {
-                        Log.w(TAG, "10249126\t- " + l);
+                        Launcher.addDumpLog(TAG, "10249126\t- " + l, true);
                     }
 
                     LauncherAppState.getLauncherProvider().updateMaxScreenId(maxScreenId);
@@ -1945,7 +1950,7 @@
                     }
                     LauncherAppState.getLauncherProvider().updateMaxItemId(maxItemId);
                 } else {
-                    Log.w(TAG, "10249126 - loadWorkspace - !loadedOldDb");
+                    Launcher.addDumpLog(TAG, "10249126 - loadWorkspace - !loadedOldDb", true);
                     TreeMap<Integer, Long> orderedScreens = loadWorkspaceScreensDb(mContext);
                     for (Integer i : orderedScreens.keySet()) {
                         sBgWorkspaceScreens.add(orderedScreens.get(i));
@@ -1969,11 +1974,10 @@
                         sBgWorkspaceScreens.removeAll(unusedScreens);
 
                         // Dump the sBgWorkspaceScreens
-                        Log.w(TAG, "10249126 - updateWorkspaceScreenOrder - sBgWorkspaceScreens");
+                        Launcher.addDumpLog(TAG, "10249126 - updateWorkspaceScreenOrder - sBgWorkspaceScreens", true);
                         for (Long l : sBgWorkspaceScreens) {
-                            Log.w(TAG, "10249126\t- " + l);
+                            Launcher.addDumpLog(TAG, "10249126\t- " + l, true);
                         }
-
                         updateWorkspaceScreenOrder(context, sBgWorkspaceScreens);
                     }
                 }
@@ -2126,12 +2130,12 @@
 
         private void bindWorkspaceScreens(final Callbacks oldCallbacks,
                 final ArrayList<Long> orderedScreens) {
-            Log.w(TAG, "10249126 - bindWorkspaceScreens()");
+            Launcher.addDumpLog(TAG, "10249126 - bindWorkspaceScreens()", true);
 
             // Dump the orderedScreens
-            Log.w(TAG, "10249126 - orderedScreens");
+            Launcher.addDumpLog(TAG, "10249126 - orderedScreens", true);
             for (Long l : sBgWorkspaceScreens) {
-                Log.w(TAG, "10249126\t- " + l);
+                Launcher.addDumpLog(TAG, "10249126\t- " + l, true);
             }
 
             final Runnable r = new Runnable() {
@@ -2613,6 +2617,17 @@
                     }
                 }
             });
+
+            // Write all the logs to disk
+            Launcher.addDumpLog(TAG, "10249126 - PackageUpdatedTask - dumping logs to disk", true);
+            mHandler.post(new Runnable() {
+                public void run() {
+                    Callbacks cb = mCallbacks != null ? mCallbacks.get() : null;
+                    if (callbacks == cb && cb != null) {
+                        callbacks.dumpLogsToLocalData(false);
+                    }
+                }
+            });
         }
     }
 
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index e3fad4e..05f9e2d 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -40,6 +40,7 @@
 import android.graphics.Rect;
 import android.graphics.Region.Op;
 import android.graphics.drawable.Drawable;
+import android.net.Uri;
 import android.os.IBinder;
 import android.os.Parcelable;
 import android.util.AttributeSet;
@@ -455,7 +456,7 @@
     }
 
     public void removeAllWorkspaceScreens() {
-        Log.w(TAG, "10249126 - removeAllWorkspaceScreens()");
+        Launcher.addDumpLog(TAG, "10249126 - removeAllWorkspaceScreens()", true);
         // Remove the pages and clear the screen models
         removeAllViews();
         mScreenOrder.clear();
@@ -466,7 +467,7 @@
         // Find the index to insert this view into.  If the empty screen exists, then
         // insert it before that.
         int insertIndex = mScreenOrder.indexOf(EXTRA_EMPTY_SCREEN_ID);
-        Log.w(TAG, "10249126 - insertNewWorkspaceScreenBeforeEmptyScreen(" + insertIndex + ")");
+        Launcher.addDumpLog(TAG, "10249126 - insertNewWorkspaceScreenBeforeEmptyScreen(" + insertIndex + ")", true);
         if (insertIndex < 0) {
             insertIndex = mScreenOrder.size();
         }
@@ -478,7 +479,9 @@
     }
 
     public long insertNewWorkspaceScreen(long screenId, int insertIndex) {
-        Log.w(TAG, "10249126 - insertNewWorkspaceScreen(" + screenId + ", " + insertIndex + ")");
+        String log = "10249126 - insertNewWorkspaceScreen(" + screenId + ", " + insertIndex + ")";
+        Launcher.addDumpLog(TAG, log, true);
+
         CellLayout newScreen = (CellLayout)
                 mLauncher.getLayoutInflater().inflate(R.layout.workspace_screen, null);
 
@@ -491,7 +494,7 @@
     }
 
     public void createCustomContentPage() {
-        Log.w(TAG, "10249126 - createCustomContentPage()");
+        Launcher.addDumpLog(TAG, "10249126 - createCustomContentPage()", true);
         CellLayout customScreen = (CellLayout)
                 mLauncher.getLayoutInflater().inflate(R.layout.workspace_screen, null);
 
@@ -526,7 +529,12 @@
     }
 
     public long commitExtraEmptyScreen() {
-        Log.w(TAG, "10249126 - commitExtraEmptyScreen()");
+        Launcher.addDumpLog(TAG, "10249126 - commitExtraEmptyScreen()", true);
+
+        // Write all the logs to disk
+        Launcher.addDumpLog(TAG, "10249126 - commitExtraEmptyScreen() - dumping logs to disk", true);
+        mLauncher.dumpLogsToLocalData(false);
+
         CellLayout cl = mWorkspaceScreens.get(EXTRA_EMPTY_SCREEN_ID);
         mWorkspaceScreens.remove(EXTRA_EMPTY_SCREEN_ID);
         mScreenOrder.remove(EXTRA_EMPTY_SCREEN_ID);
@@ -548,13 +556,13 @@
     }
 
     public CellLayout getScreenWithId(long screenId) {
-        Log.w(TAG, "10249126 - getScreenWithId(" + screenId + ")");
+        Launcher.addDumpLog(TAG, "10249126 - getScreenWithId(" + screenId + ")", true);
         CellLayout layout = mWorkspaceScreens.get(screenId);
         return layout;
     }
 
     public long getIdForScreen(CellLayout layout) {
-        Log.w(TAG, "10249126 - getIdForScreen()");
+        Launcher.addDumpLog(TAG, "10249126 - getIdForScreen()", true);
         Iterator<Long> iter = mWorkspaceScreens.keySet().iterator();
         while (iter.hasNext()) {
             long id = iter.next();
@@ -566,7 +574,7 @@
     }
 
     public int getPageIndexForScreenId(long screenId) {
-        Log.w(TAG, "10249126 - getPageIndexForScreenId(" + screenId + ")");
+        Launcher.addDumpLog(TAG, "10249126 - getPageIndexForScreenId(" + screenId + ")", true);
         return indexOfChild(mWorkspaceScreens.get(screenId));
     }
 
@@ -578,7 +586,7 @@
     }
 
     public boolean hasNonCustomEmptyScreens() {
-        Log.w(TAG, "10249126 - hasNonCustomEmptyScreens()");
+        Launcher.addDumpLog(TAG, "10249126 - hasNonCustomEmptyScreens()", true);
         Iterator<Long> iter = mWorkspaceScreens.keySet().iterator();
         while (iter.hasNext()) {
             long id = iter.next();
@@ -599,7 +607,7 @@
             return;
         }
 
-        Log.w(TAG, "10249126 - stripEmptyScreens()");
+        Launcher.addDumpLog(TAG, "10249126 - stripEmptyScreens()", true);
 
         int currentPage = getNextPage();
         ArrayList<Long> removeScreens = new ArrayList<Long>();
@@ -612,7 +620,7 @@
 
         int pageShift = 0;
         for (Long id: removeScreens) {
-            Log.w(TAG, "10249126 - \tremove(" + id + ")");
+            Launcher.addDumpLog(TAG, "10249126 - \tremove(" + id + ")", true);
             CellLayout cl = mWorkspaceScreens.get(id);
             mWorkspaceScreens.remove(id);
             mScreenOrder.remove(id);
@@ -729,7 +737,7 @@
             // TODO: This branch occurs when the workspace is adding views
             // outside of the defined grid
             // maybe we should be deleting these items from the LauncherModel?
-            Log.w(TAG, "Failed to add to item at (" + lp.cellX + "," + lp.cellY + ") to CellLayout");
+            Launcher.addDumpLog(TAG, "Failed to add to item at (" + lp.cellX + "," + lp.cellY + ") to CellLayout", true);
         }
 
         if (!(child instanceof Folder)) {
@@ -1633,7 +1641,7 @@
             mScreenOrder.add(getIdForScreen(cl));
         }
 
-        Log.w(TAG, "10249126 - onEndReordering()");
+        Launcher.addDumpLog(TAG, "10249126 - onEndReordering()", true);
         mLauncher.getModel().updateWorkspaceScreenOrder(mLauncher, mScreenOrder);
 
         // Re-enable auto layout transitions for page deletion.
@@ -3513,18 +3521,19 @@
         }
     }
 
-    ArrayList<ComponentName> stripDuplicateApps() {
+    ArrayList<ComponentName> getUniqueComponents(boolean stripDuplicates, ArrayList<ComponentName> duplicates) {
         ArrayList<ComponentName> uniqueIntents = new ArrayList<ComponentName>();
-        stripDuplicateApps((CellLayout) mLauncher.getHotseat().getLayout(), uniqueIntents);
+        getUniqueIntents((CellLayout) mLauncher.getHotseat().getLayout(), uniqueIntents, duplicates, false);
         int count = getChildCount();
         for (int i = 0; i < count; i++) {
             CellLayout cl = (CellLayout) getChildAt(i);
-            stripDuplicateApps(cl, uniqueIntents);
+            getUniqueIntents(cl, uniqueIntents, duplicates, false);
         }
         return uniqueIntents;
     }
 
-    void stripDuplicateApps(CellLayout cl, ArrayList<ComponentName> uniqueIntents) {
+    void getUniqueIntents(CellLayout cl, ArrayList<ComponentName> uniqueIntents,
+            ArrayList<ComponentName> duplicates, boolean stripDuplicates) {
         int count = cl.getShortcutsAndWidgets().getChildCount();
 
         ArrayList<View> children = new ArrayList<View>();
@@ -3541,15 +3550,24 @@
                 ShortcutInfo si = (ShortcutInfo) info;
                 ComponentName cn = si.intent.getComponent();
 
-                if (si.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) {
+                Uri dataUri = si.intent.getData();
+                // If dataUri is not null / empty or if this component isn't one that would
+                // have previously showed up in the AllApps list, then this is a widget-type
+                // shortcut, so ignore it.
+                if (dataUri != null && !dataUri.equals(Uri.EMPTY)) {
                     continue;
                 }
 
                 if (!uniqueIntents.contains(cn)) {
                     uniqueIntents.add(cn);
                 } else {
-                    cl.removeViewInLayout(v);
-                    LauncherModel.deleteItemFromDatabase(mLauncher, si);
+                    if (stripDuplicates) {
+                        cl.removeViewInLayout(v);
+                        LauncherModel.deleteItemFromDatabase(mLauncher, si);
+                    }
+                    if (duplicates != null) {
+                        duplicates.add(cn);
+                    }
                 }
             }
             if (v instanceof FolderIcon) {
@@ -3560,14 +3578,24 @@
                         ShortcutInfo si = (ShortcutInfo) items.get(j).getTag();
                         ComponentName cn = si.intent.getComponent();
 
-                        if (si.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) {
+                        Uri dataUri = si.intent.getData();
+                        // If dataUri is not null / empty or if this component isn't one that would
+                        // have previously showed up in the AllApps list, then this is a widget-type
+                        // shortcut, so ignore it.
+                        if (dataUri != null && !dataUri.equals(Uri.EMPTY)) {
                             continue;
                         }
+
                         if (!uniqueIntents.contains(cn)) {
                             uniqueIntents.add(cn);
-                        } else {
-                            fi.getFolderInfo().remove(si);
-                            LauncherModel.deleteItemFromDatabase(mLauncher, si);
+                        }  else {
+                            if (stripDuplicates) {
+                                fi.getFolderInfo().remove(si);
+                                LauncherModel.deleteItemFromDatabase(mLauncher, si);
+                            }
+                            if (duplicates != null) {
+                                duplicates.add(cn);
+                            }
                         }
                     }
                 }