am c29121ba: (-s ours) am 0961a545: (-s ours) Import translations. DO NOT MERGE

* commit 'c29121baeebe177386ba953ef3edc324c84ef6c3':
  Import translations. DO NOT MERGE
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index 62e5266..6b09789 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -72,9 +72,9 @@
     <string name="permlab_uninstall_shortcut" msgid="864595034498083837">"Verknüpfungen deinstallieren"</string>
     <string name="permdesc_uninstall_shortcut" msgid="5134129545001836849">"Ermöglicht einer App das Entfernen von Verknüpfungen ohne Eingreifen des Nutzers"</string>
     <string name="permlab_read_settings" msgid="1941457408239617576">"Einstellungen und Verknüpfungen auf dem Startbildschirm lesen"</string>
-    <string name="permdesc_read_settings" msgid="5833423719057558387">"Ermöglicht einer App, die Einstellungen und Verknüpfungen auf dem Startbildschirm zu lesen"</string>
+    <string name="permdesc_read_settings" msgid="5833423719057558387">"Ermöglicht der App, die Einstellungen und Verknüpfungen auf dem Startbildschirm zu lesen"</string>
     <string name="permlab_write_settings" msgid="3574213698004620587">"Einstellungen und Verknüpfungen für den Startbildschirm schreiben"</string>
-    <string name="permdesc_write_settings" msgid="5440712911516509985">"Ermöglicht einer App, die Einstellungen und Verknüpfungen auf dem Startbildschirm zu ändern"</string>
+    <string name="permdesc_write_settings" msgid="5440712911516509985">"Ermöglicht der App, die Einstellungen und Verknüpfungen auf dem Startbildschirm zu ändern"</string>
     <string name="gadget_error_text" msgid="6081085226050792095">"Problem beim Laden des Widgets"</string>
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Dies ist eine Systemanwendung, die nicht deinstalliert werden kann."</string>
     <string name="dream_name" msgid="1530253749244328964">"Rocket Launcher"</string>
diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml
index 12f2428..81126fd 100644
--- a/res/values-sw/strings.xml
+++ b/res/values-sw/strings.xml
@@ -67,7 +67,7 @@
     <string name="cab_widget_selection_text" msgid="1833458597831541241">"Wijeti 1 imechaguliwa"</string>
     <string name="cab_folder_selection_text" msgid="7999992513806132118">"Folda 1 limechaguliwa"</string>
     <string name="cab_shortcut_selection_text" msgid="2103811025667946450">"Njia 1 ya mkato imechaguliwa"</string>
-    <string name="permlab_install_shortcut" msgid="5632423390354674437">"sakinisha njia za mkato"</string>
+    <string name="permlab_install_shortcut" msgid="5632423390354674437">"kuweka njia za mkato"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Huruhusu programu kuongeza njia za mkato bila mtumiaji kuingilia kati."</string>
     <string name="permlab_uninstall_shortcut" msgid="864595034498083837">"ondoa njia za mikato"</string>
     <string name="permdesc_uninstall_shortcut" msgid="5134129545001836849">"Huruhusu programu kuondoa njia za mikato bila mtumiaji kuingilia kati."</string>
diff --git a/src/com/android/launcher3/AppInfo.java b/src/com/android/launcher3/AppInfo.java
index f85f691..c16e98f 100644
--- a/src/com/android/launcher3/AppInfo.java
+++ b/src/com/android/launcher3/AppInfo.java
@@ -26,6 +26,7 @@
 import android.util.Log;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashMap;
 
 /**
@@ -137,7 +138,8 @@
         return "ApplicationInfo(title=" + title.toString() + " id=" + this.id
                 + " type=" + this.itemType + " container=" + this.container
                 + " screen=" + screenId + " cellX=" + cellX + " cellY=" + cellY
-                + " spanX=" + spanX + " spanY=" + spanY + " dropPos=" + dropPos + ")";
+                + " spanX=" + spanX + " spanY=" + spanY + " dropPos=" + Arrays.toString(dropPos)
+                + ")";
     }
 
     public static void dumpApplicationInfoList(String tag, String label, ArrayList<AppInfo> list) {
diff --git a/src/com/android/launcher3/AppsCustomizeTabHost.java b/src/com/android/launcher3/AppsCustomizeTabHost.java
index bb7f045..c6455c2 100644
--- a/src/com/android/launcher3/AppsCustomizeTabHost.java
+++ b/src/com/android/launcher3/AppsCustomizeTabHost.java
@@ -29,6 +29,7 @@
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityManager;
 import android.widget.FrameLayout;
 import android.widget.LinearLayout;
 import android.widget.TabHost;
@@ -430,6 +431,14 @@
             // prevent slowing down the animation)
             mAppsCustomizePane.loadAssociatedPages(mAppsCustomizePane.getCurrentPage());
 
+            // Opening apps, need to announce what page we are on.
+            AccessibilityManager am = (AccessibilityManager)
+                    getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
+            if (am.isEnabled()) {
+                // Notify the user when the page changes
+                announceForAccessibility(mAppsCustomizePane.getCurrentPageDescription());
+            }
+
             // Going from Workspace -> All Apps
             // NOTE: We should do this at the end since we check visibility state in some of the
             // cling initialization/dismiss code above.
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index c180d32..95300c1 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -28,6 +28,7 @@
 import android.util.Log;
 import android.util.TypedValue;
 import android.view.MotionEvent;
+import android.view.ViewConfiguration;
 import android.widget.TextView;
 
 /**
@@ -60,6 +61,8 @@
     private int mPressedOutlineColor;
     private int mPressedGlowColor;
 
+    private float mSlop;
+
     private int mTextColor;
     private boolean mShadowsEnabled = true;
     private boolean mIsTextVisible;
@@ -272,6 +275,11 @@
 
                 mLongPressHelper.cancelLongPress();
                 break;
+            case MotionEvent.ACTION_MOVE:
+                if (!Utilities.pointInView(this, event.getX(), event.getY(), mSlop)) {
+                    mLongPressHelper.cancelLongPress();
+                }
+                break;
         }
         return result;
     }
@@ -356,6 +364,7 @@
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
         if (mBackground != null) mBackground.setCallback(this);
+        mSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
     }
 
     @Override
diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java
index fb226e5..dcc55af 100644
--- a/src/com/android/launcher3/Folder.java
+++ b/src/com/android/launcher3/Folder.java
@@ -391,7 +391,7 @@
         // We rearrange the items in case there are any empty gaps
         setupContentForNumItems(count);
 
-        // If our folder has too many items we prune them from the list. This is an issue 
+        // If our folder has too many items we prune them from the list. This is an issue
         // when upgrading from the old Folders implementation which could contain an unlimited
         // number of items.
         for (ShortcutInfo item: overflow) {
@@ -583,7 +583,7 @@
         // by another item.
         if (mContent.getChildAt(item.cellX, item.cellY) != null || item.cellX < 0 || item.cellY < 0
                 || item.cellX >= mContent.getCountX() || item.cellY >= mContent.getCountY()) {
-            // This shouldn't happen, log it. 
+            // This shouldn't happen, log it.
             Log.e(TAG, "Folder order not properly persisted during bind");
             if (!findAndSetEmptyCells(item)) {
                 return null;
@@ -1177,7 +1177,7 @@
         Runnable cleanUpRunnable = null;
 
         // If we are coming from All Apps space, we need to remove the extra empty screen (which is
-        // normally done in Workspace#onDropExternal, as well zoom back in and close the folder.
+        // normally done in Workspace#onDropExternal, as well zoom back in.
         if (d.dragSource != mLauncher.getWorkspace() && !(d.dragSource instanceof Folder)) {
             cleanUpRunnable = new Runnable() {
                 @Override
@@ -1185,7 +1185,6 @@
                     mLauncher.getWorkspace().removeExtraEmptyScreen(false, new Runnable() {
                         @Override
                         public void run() {
-                            mLauncher.closeFolder();
                             mLauncher.exitSpringLoadedDragModeDelayed(true,
                                     Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT_FOLDER_CLOSE,
                                     null);
diff --git a/src/com/android/launcher3/FolderIcon.java b/src/com/android/launcher3/FolderIcon.java
index 71a7461..25c4962 100644
--- a/src/com/android/launcher3/FolderIcon.java
+++ b/src/com/android/launcher3/FolderIcon.java
@@ -33,6 +33,7 @@
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.View;
+import android.view.ViewConfiguration;
 import android.view.ViewGroup;
 import android.view.animation.AccelerateInterpolator;
 import android.view.animation.DecelerateInterpolator;
@@ -105,6 +106,8 @@
     boolean mAnimating = false;
     private Rect mOldBounds = new Rect();
 
+    private float mSlop;
+
     private PreviewItemDrawingParams mParams = new PreviewItemDrawingParams(0, 0, 0, 0);
     private PreviewItemDrawingParams mAnimParams = new PreviewItemDrawingParams(0, 0, 0, 0);
     private ArrayList<ShortcutInfo> mHiddenItems = new ArrayList<ShortcutInfo>();
@@ -386,7 +389,7 @@
 
     public void performDestroyAnimation(final View finalView, Runnable onCompleteRunnable) {
         Drawable animateDrawable = ((TextView) finalView).getCompoundDrawables()[1];
-        computePreviewDrawingParams(animateDrawable.getIntrinsicWidth(), 
+        computePreviewDrawingParams(animateDrawable.getIntrinsicWidth(),
                 finalView.getMeasuredWidth());
 
         // This will animate the first item from it's position as an icon into its
@@ -703,11 +706,22 @@
             case MotionEvent.ACTION_UP:
                 mLongPressHelper.cancelLongPress();
                 break;
+            case MotionEvent.ACTION_MOVE:
+                if (!Utilities.pointInView(this, event.getX(), event.getY(), mSlop)) {
+                    mLongPressHelper.cancelLongPress();
+                }
+                break;
         }
         return result;
     }
 
     @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        mSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
+    }
+
+    @Override
     public void cancelLongPress() {
         super.cancelLongPress();
 
diff --git a/src/com/android/launcher3/FolderInfo.java b/src/com/android/launcher3/FolderInfo.java
index 42e2ec3..61fa7e5 100644
--- a/src/com/android/launcher3/FolderInfo.java
+++ b/src/com/android/launcher3/FolderInfo.java
@@ -19,6 +19,7 @@
 import android.content.ContentValues;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 
 /**
  * Represents a folder containing shortcuts or apps.
@@ -114,6 +115,6 @@
         return "FolderInfo(id=" + this.id + " type=" + this.itemType
                 + " container=" + this.container + " screen=" + screenId
                 + " cellX=" + cellX + " cellY=" + cellY + " spanX=" + spanX
-                + " spanY=" + spanY + " dropPos=" + dropPos + ")";
+                + " spanY=" + spanY + " dropPos=" + Arrays.toString(dropPos) + ")";
     }
 }
diff --git a/src/com/android/launcher3/ItemInfo.java b/src/com/android/launcher3/ItemInfo.java
index 3fe4c60..12bbee7 100644
--- a/src/com/android/launcher3/ItemInfo.java
+++ b/src/com/android/launcher3/ItemInfo.java
@@ -23,6 +23,7 @@
 
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
+import java.util.Arrays;
 
 /**
  * Represents an item in the launcher.
@@ -131,7 +132,7 @@
      * 
      * @param values
      */
-    void onAddToDatabase(ContentValues values) { 
+    void onAddToDatabase(ContentValues values) {
         values.put(LauncherSettings.BaseLauncherColumns.ITEM_TYPE, itemType);
         values.put(LauncherSettings.Favorites.CONTAINER, container);
         values.put(LauncherSettings.Favorites.SCREEN, screenId);
@@ -139,6 +140,11 @@
         values.put(LauncherSettings.Favorites.CELLY, cellY);
         values.put(LauncherSettings.Favorites.SPANX, spanX);
         values.put(LauncherSettings.Favorites.SPANY, spanY);
+
+        if (screenId == Workspace.EXTRA_EMPTY_SCREEN_ID) {
+            // We should never persist an item on the extra empty screen.
+            throw new RuntimeException("Screen id should not be EXTRA_EMPTY_SCREEN_ID");
+        }
     }
 
     void updateValuesWithCoordinates(ContentValues values, int cellX, int cellY) {
@@ -182,6 +188,6 @@
     public String toString() {
         return "Item(id=" + this.id + " type=" + this.itemType + " container=" + this.container
             + " screen=" + screenId + " cellX=" + cellX + " cellY=" + cellY + " spanX=" + spanX
-            + " spanY=" + spanY + " dropPos=" + dropPos + ")";
+            + " spanY=" + spanY + " dropPos=" + Arrays.toString(dropPos) + ")";
     }
 }
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index 5ddafea..ad01019 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -30,7 +30,7 @@
     private static final String TAG = "LauncherAppState";
     private static final String SHARED_PREFERENCES_KEY = "com.android.launcher3.prefs";
 
-    private static final boolean DEBUG = false;
+    private static final boolean DEBUG = true;  // STOPSHIP(cwren) temporary for debugging
 
     private final AppFilter mAppFilter;
     private final BuildInfo mBuildInfo;
diff --git a/src/com/android/launcher3/LauncherAppWidgetHostView.java b/src/com/android/launcher3/LauncherAppWidgetHostView.java
index 51a649a..f47fd13 100644
--- a/src/com/android/launcher3/LauncherAppWidgetHostView.java
+++ b/src/com/android/launcher3/LauncherAppWidgetHostView.java
@@ -21,6 +21,7 @@
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.View;
+import android.view.ViewConfiguration;
 import android.view.ViewGroup;
 import android.widget.RemoteViews;
 
@@ -36,6 +37,8 @@
     private int mPreviousOrientation;
     private DragLayer mDragLayer;
 
+    private float mSlop;
+
     public LauncherAppWidgetHostView(Context context) {
         super(context);
         mContext = context;
@@ -90,6 +93,11 @@
             case MotionEvent.ACTION_CANCEL:
                 mLongPressHelper.cancelLongPress();
                 break;
+            case MotionEvent.ACTION_MOVE:
+                if (!Utilities.pointInView(this, ev.getX(), ev.getY(), mSlop)) {
+                    mLongPressHelper.cancelLongPress();
+                }
+                break;
         }
 
         // Otherwise continue letting touch events fall through to children
@@ -104,11 +112,22 @@
             case MotionEvent.ACTION_CANCEL:
                 mLongPressHelper.cancelLongPress();
                 break;
+            case MotionEvent.ACTION_MOVE:
+                if (!Utilities.pointInView(this, ev.getX(), ev.getY(), mSlop)) {
+                    mLongPressHelper.cancelLongPress();
+                }
+                break;
         }
         return false;
     }
 
     @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        mSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
+    }
+
+    @Override
     public void cancelLongPress() {
         super.cancelLongPress();
         mLongPressHelper.cancelLongPress();
diff --git a/src/com/android/launcher3/LauncherBackupAgentHelper.java b/src/com/android/launcher3/LauncherBackupAgentHelper.java
index de6aedd..7dd8cde 100644
--- a/src/com/android/launcher3/LauncherBackupAgentHelper.java
+++ b/src/com/android/launcher3/LauncherBackupAgentHelper.java
@@ -61,7 +61,7 @@
     @Override
     public void onCreate() {
         boolean restoreEnabled = 0 != Settings.Secure.getInt(
-                getContentResolver(), SETTING_RESTORE_ENABLED, 0);
+                getContentResolver(), SETTING_RESTORE_ENABLED, 1);
         if (VERBOSE) Log.v(TAG, "restore is " + (restoreEnabled ? "enabled" : "disabled"));
 
         addHelper(LauncherBackupHelper.LAUNCHER_PREFS_PREFIX,
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index d8645aa..044ddbb 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -69,12 +69,13 @@
  */
 public class LauncherModel extends BroadcastReceiver {
     static final boolean DEBUG_LOADERS = false;
+    private static final boolean DEBUG_RECEIVER = true;  // STOPSHIP(cwren) temporary for debugging
+
     static final String TAG = "Launcher.Model";
 
     // true = use a "More Apps" folder for non-workspace apps on upgrade
     // false = strew non-workspace apps across the workspace on upgrade
     public static final boolean UPGRADE_USE_MORE_APPS_FOLDER = false;
-
     public static final int LOADER_FLAG_NONE = 0;
     public static final int LOADER_FLAG_CLEAR_WORKSPACE = 1 << 0;
     public static final int LOADER_FLAG_MIGRATE_SHORTCUTS = 1 << 1;
@@ -498,7 +499,9 @@
         }
 
         // Clear any deferred bind runnables
-        mDeferredBindRunnables.clear();
+        synchronized (mDeferredBindRunnables) {
+            mDeferredBindRunnables.clear();
+        }
         // Remove any queued bind runnables
         mHandler.cancelAllRunnablesOfType(MAIN_THREAD_BINDING_RUNNABLE);
         // Unbind all the workspace items
@@ -1161,7 +1164,7 @@
      */
     @Override
     public void onReceive(Context context, Intent intent) {
-        if (DEBUG_LOADERS) Log.d(TAG, "onReceive intent=" + intent);
+        if (DEBUG_RECEIVER) Log.d(TAG, "onReceive intent=" + intent);
 
         final String action = intent.getAction();
 
@@ -1315,7 +1318,9 @@
 
             // Clear any deferred bind-runnables from the synchronized load process
             // We must do this before any loading/binding is scheduled below.
-            mDeferredBindRunnables.clear();
+            synchronized (mDeferredBindRunnables) {
+                mDeferredBindRunnables.clear();
+            }
 
             // Don't bother to start the thread if we know it's not going to do anything
             if (mCallbacks != null && mCallbacks.get() != null) {
@@ -1337,10 +1342,15 @@
     void bindRemainingSynchronousPages() {
         // Post the remaining side pages to be loaded
         if (!mDeferredBindRunnables.isEmpty()) {
-            for (final Runnable r : mDeferredBindRunnables) {
+            Runnable[] deferredBindRunnables = null;
+            synchronized (mDeferredBindRunnables) {
+                deferredBindRunnables = mDeferredBindRunnables.toArray(
+                        new Runnable[mDeferredBindRunnables.size()]);
+                mDeferredBindRunnables.clear();
+            }
+            for (final Runnable r : deferredBindRunnables) {
                 mHandler.post(r, MAIN_THREAD_BINDING_RUNNABLE);
             }
-            mDeferredBindRunnables.clear();
         }
     }
 
@@ -2367,7 +2377,9 @@
                     }
                 };
                 if (postOnMainThread) {
-                    deferredBindRunnables.add(r);
+                    synchronized (deferredBindRunnables) {
+                        deferredBindRunnables.add(r);
+                    }
                 } else {
                     runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE);
                 }
@@ -2384,7 +2396,9 @@
                     }
                 };
                 if (postOnMainThread) {
-                    deferredBindRunnables.add(r);
+                    synchronized (deferredBindRunnables) {
+                        deferredBindRunnables.add(r);
+                    }
                 } else {
                     runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE);
                 }
@@ -2506,7 +2520,9 @@
 
             // Load all the remaining pages (if we are loading synchronously, we want to defer this
             // work until after the first render)
-            mDeferredBindRunnables.clear();
+            synchronized (mDeferredBindRunnables) {
+                mDeferredBindRunnables.clear();
+            }
             bindWorkspaceItems(oldCallbacks, otherWorkspaceItems, otherAppWidgets, otherFolders,
                     (isLoadingSynchronously ? mDeferredBindRunnables : null));
 
@@ -2528,7 +2544,9 @@
                 }
             };
             if (isLoadingSynchronously) {
-                mDeferredBindRunnables.add(r);
+                synchronized (mDeferredBindRunnables) {
+                    mDeferredBindRunnables.add(r);
+                }
             } else {
                 runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE);
             }
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index 9a004f2..e43b727 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -75,7 +75,7 @@
 
     private static final String DATABASE_NAME = "launcher.db";
 
-    private static final int DATABASE_VERSION = 18;
+    private static final int DATABASE_VERSION = 19;
 
     static final String OLD_AUTHORITY = "com.android.launcher2.settings";
     static final String AUTHORITY = ProviderConfig.AUTHORITY;
@@ -503,10 +503,34 @@
         }
 
         private void removeOrphanedItems(SQLiteDatabase db) {
-            db.execSQL("DELETE FROM " + TABLE_FAVORITES + " WHERE " +
+            // Delete items directly on the workspace who's screen id doesn't exist
+            //  "DELETE FROM favorites WHERE screen NOT IN (SELECT _id FROM workspaceScreens)
+            //   AND container = -100"
+            String removeOrphanedDesktopItems = "DELETE FROM " + TABLE_FAVORITES +
+                    " WHERE " +
                     LauncherSettings.Favorites.SCREEN + " NOT IN (SELECT " +
-                    LauncherSettings.WorkspaceScreens._ID + " FROM " + TABLE_WORKSPACE_SCREENS +
-                    ")");
+                    LauncherSettings.WorkspaceScreens._ID + " FROM " + TABLE_WORKSPACE_SCREENS + ")" +
+                    " AND " +
+                    LauncherSettings.Favorites.CONTAINER + " = " +
+                    LauncherSettings.Favorites.CONTAINER_DESKTOP;
+            db.execSQL(removeOrphanedDesktopItems);
+
+            // Delete items contained in folders which no longer exist (after above statement)
+            //  "DELETE FROM favorites  WHERE container <> -100 AND container <> -101 AND container
+            //   NOT IN (SELECT _id FROM favorites WHERE itemType = 2)"
+            String removeOrphanedFolderItems = "DELETE FROM " + TABLE_FAVORITES +
+                    " WHERE " +
+                    LauncherSettings.Favorites.CONTAINER + " <> " +
+                    LauncherSettings.Favorites.CONTAINER_DESKTOP +
+                    " AND "
+                    + LauncherSettings.Favorites.CONTAINER + " <> " +
+                    LauncherSettings.Favorites.CONTAINER_HOTSEAT +
+                    " AND "
+                    + LauncherSettings.Favorites.CONTAINER + " NOT IN (SELECT " +
+                    LauncherSettings.Favorites._ID + " FROM " + TABLE_FAVORITES +
+                    " WHERE " + LauncherSettings.Favorites.ITEM_TYPE + " = " +
+                    LauncherSettings.Favorites.ITEM_TYPE_FOLDER + ")";
+            db.execSQL(removeOrphanedFolderItems);
         }
 
         private void setFlagJustLoadedOldDb() {
@@ -810,14 +834,17 @@
             }
 
             if (version < 18) {
+                // No-op
+                version = 18;
+            }
+
+            if (version < 19) {
                 // Due to a data loss bug, some users may have items associated with screen ids
                 // which no longer exist. Since this can cause other problems, and since the user
                 // will never see these items anyway, we use database upgrade as an opportunity to
                 // clean things up.
-
-                // TODO: this needs to be fixed, currently causes data loss.
-                //removeOrphanedItems(db);
-                version = 18;
+                removeOrphanedItems(db);
+                version = 19;
             }
 
             if (version != DATABASE_VERSION) {
diff --git a/src/com/android/launcher3/MainThreadExecutor.java b/src/com/android/launcher3/MainThreadExecutor.java
new file mode 100644
index 0000000..866b17c
--- /dev/null
+++ b/src/com/android/launcher3/MainThreadExecutor.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.launcher3;
+
+import android.os.Handler;
+import android.os.Looper;
+
+import java.util.List;
+import java.util.concurrent.AbstractExecutorService;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * An executor service that executes its tasks on the main thread.
+ *
+ * Shutting down this executor is not supported.
+ */
+public class MainThreadExecutor extends AbstractExecutorService {
+
+    private Handler mHandler = new Handler(Looper.getMainLooper());
+
+    @Override
+    public void execute(Runnable runnable) {
+        if (Looper.getMainLooper() == Looper.myLooper()) {
+            runnable.run();
+        } else {
+            mHandler.post(runnable);
+        }
+    }
+
+    /**
+     * Not supported and throws an exception when used.
+     */
+    @Override
+    @Deprecated
+    public void shutdown() {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * Not supported and throws an exception when used.
+     */
+    @Override
+    @Deprecated
+    public List<Runnable> shutdownNow() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean isShutdown() {
+        return false;
+    }
+
+    @Override
+    public boolean isTerminated() {
+        return false;
+    }
+
+    /**
+     * Not supported and throws an exception when used.
+     */
+    @Override
+    @Deprecated
+    public boolean awaitTermination(long l, TimeUnit timeUnit) throws InterruptedException {
+        throw new UnsupportedOperationException();
+    }
+}
diff --git a/src/com/android/launcher3/ShortcutInfo.java b/src/com/android/launcher3/ShortcutInfo.java
index 5afa784..92b582d 100644
--- a/src/com/android/launcher3/ShortcutInfo.java
+++ b/src/com/android/launcher3/ShortcutInfo.java
@@ -27,6 +27,7 @@
 import android.util.Log;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 
 /**
  * Represents a launchable icon on the workspaces and in folders.
@@ -226,7 +227,7 @@
         return "ShortcutInfo(title=" + title.toString() + "intent=" + intent + "id=" + this.id
                 + " type=" + this.itemType + " container=" + this.container + " screen=" + screenId
                 + " cellX=" + cellX + " cellY=" + cellY + " spanX=" + spanX + " spanY=" + spanY
-                + " dropPos=" + dropPos + ")";
+                + " dropPos=" + Arrays.toString(dropPos) + ")";
     }
 
     public static void dumpShortcutInfoList(String tag, String label,
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index cbc9785..8deadf1 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -305,6 +305,17 @@
         return scale;
     }
 
+    /**
+     * Utility method to determine whether the given point, in local coordinates,
+     * is inside the view, where the area of the view is expanded by the slop factor.
+     * This method is called while processing touch-move events to determine if the event
+     * is still within the view.
+     */
+    public static boolean pointInView(View v, float localX, float localY, float slop) {
+        return localX >= -slop && localY >= -slop && localX < (v.getWidth() + slop) &&
+                localY < (v.getHeight() + slop);
+    }
+
     private static void initStatics(Context context) {
         final Resources resources = context.getResources();
         final DisplayMetrics metrics = resources.getDisplayMetrics();
diff --git a/src/com/android/launcher3/WidgetPreviewLoader.java b/src/com/android/launcher3/WidgetPreviewLoader.java
index 36152f8..5dad2a8 100644
--- a/src/com/android/launcher3/WidgetPreviewLoader.java
+++ b/src/com/android/launcher3/WidgetPreviewLoader.java
@@ -35,6 +35,8 @@
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
 
 abstract class SoftReferenceThreadLocal<T> {
     private ThreadLocal<SoftReference<T>> mThreadLocal;
@@ -137,6 +139,8 @@
     private final ArrayList<SoftReference<Bitmap>> mUnusedBitmaps;
     private final static HashSet<String> sInvalidPackages;
 
+    private final MainThreadExecutor mMainThreadExecutor = new MainThreadExecutor();
+
     static {
         sInvalidPackages = new HashSet<String>();
     }
@@ -492,7 +496,8 @@
 
         Drawable drawable = null;
         if (previewImage != 0) {
-            drawable = mPackageManager.getDrawable(packageName, previewImage, null);
+            drawable = mutateOnMainThread(
+                    mPackageManager.getDrawable(packageName, previewImage, null));
             if (drawable == null) {
                 Log.w(TAG, "Can't load widget preview drawable 0x" +
                         Integer.toHexString(previewImage) + " for provider: " + provider);
@@ -511,6 +516,7 @@
             if (cellHSpan < 1) cellHSpan = 1;
             if (cellVSpan < 1) cellVSpan = 1;
 
+            // This Drawable is not directly drawn, so there's no need to mutate it.
             BitmapDrawable previewDrawable = (BitmapDrawable) mContext.getResources()
                     .getDrawable(R.drawable.widget_tile);
             final int previewDrawableWidth = previewDrawable
@@ -548,7 +554,7 @@
                 int yoffset =
                         (int) ((previewDrawableHeight - mAppIconSize * iconScale) / 2);
                 if (iconId > 0)
-                    icon = mIconCache.getFullResIcon(packageName, iconId);
+                    icon = mutateOnMainThread(mIconCache.getFullResIcon(packageName, iconId));
                 if (icon != null) {
                     renderDrawableToBitmap(icon, defaultPreview, hoffset,
                             yoffset, (int) (mAppIconSize * iconScale),
@@ -617,7 +623,7 @@
             c.setBitmap(null);
         }
         // Render the icon
-        Drawable icon = mIconCache.getFullResIcon(info);
+        Drawable icon = mutateOnMainThread(mIconCache.getFullResIcon(info));
 
         int paddingTop = mContext.
                 getResources().getDimensionPixelOffset(R.dimen.shortcut_preview_padding_top);
@@ -677,4 +683,19 @@
         }
     }
 
+    private Drawable mutateOnMainThread(final Drawable drawable) {
+        try {
+            return mMainThreadExecutor.submit(new Callable<Drawable>() {
+                @Override
+                public Drawable call() throws Exception {
+                    return drawable.mutate();
+                }
+            }).get();
+        } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+            throw new RuntimeException(e);
+        } catch (ExecutionException e) {
+            throw new RuntimeException(e);
+        }
+    }
 }
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 9800cf3..f7ca141 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -126,7 +126,7 @@
     private static boolean sAccessibilityEnabled;
 
     // The screen id used for the empty screen always present to the right.
-    private final static long EXTRA_EMPTY_SCREEN_ID = -201;
+    final static long EXTRA_EMPTY_SCREEN_ID = -201;
     private final static long CUSTOM_CONTENT_SCREEN_ID = -301;
 
     private HashMap<Long, CellLayout> mWorkspaceScreens = new HashMap<Long, CellLayout>();
@@ -1466,6 +1466,14 @@
         mWallpaperOffset.syncWithScroll();
     }
 
+    @Override
+    public void announceForAccessibility(CharSequence text) {
+        // Don't announce if apps is on top of us.
+        if (!mLauncher.isAllAppsVisible()) {
+            super.announceForAccessibility(text);
+        }
+    }
+
     void showOutlines() {
         if (!isSmall() && !mIsSwitchingState) {
             if (mChildrenOutlineFadeOutAnimation != null) mChildrenOutlineFadeOutAnimation.cancel();