Adding accessibility controls

> Adding 'Remove' option to workspace items
> Adding 'Add to workspace' to all apps and widget list items, which
adds the item to the first available space, giving preference to
the current workspace screen
> Adding 'App info' and 'Uninstall' options to appropriate items

Bug: 18482913
Change-Id: Ifab7423af2d9ba502b5a2771b37bb5436b3df937
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 07f3045..f9255e6 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -49,7 +49,15 @@
     private static final int SHADOW_SMALL_COLOUR = 0xCC000000;
     static final float PADDING_V = 3.0f;
 
-    private HolographicOutlineHelper mOutlineHelper;
+
+    private final Drawable mBackground;
+    private final CheckLongPressHelper mLongPressHelper;
+    private final HolographicOutlineHelper mOutlineHelper;
+
+    // TODO: Remove custom background handling code, as no instance of BubbleTextView use any
+    // background.
+    private boolean mBackgroundSizeChanged;
+
     private Bitmap mPressedBackground;
 
     private float mSlop;
@@ -58,14 +66,8 @@
     private final boolean mCustomShadowsEnabled;
     private boolean mIsTextVisible;
 
-    // TODO: Remove custom background handling code, as no instance of BubbleTextView use any
-    // background.
-    private boolean mBackgroundSizeChanged;
-    private final Drawable mBackground;
-
     private boolean mStayPressed;
     private boolean mIgnorePressedStateChange;
-    private CheckLongPressHelper mLongPressHelper;
 
     public BubbleTextView(Context context) {
         this(context, null, 0);
@@ -90,7 +92,14 @@
         } else {
             mBackground = null;
         }
-        init();
+        mLongPressHelper = new CheckLongPressHelper(this);
+
+        mOutlineHelper = HolographicOutlineHelper.obtain(getContext());
+        if (mCustomShadowsEnabled) {
+            setShadowLayer(SHADOW_LARGE_RADIUS, 0.0f, SHADOW_Y_OFFSET, SHADOW_LARGE_COLOUR);
+        }
+
+        setAccessibilityDelegate(LauncherAppState.getInstance().getAccessibilityDelegate());
     }
 
     public void onFinishInflate() {
@@ -102,15 +111,6 @@
         setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.iconTextSizePx);
     }
 
-    private void init() {
-        mLongPressHelper = new CheckLongPressHelper(this);
-
-        mOutlineHelper = HolographicOutlineHelper.obtain(getContext());
-        if (mCustomShadowsEnabled) {
-            setShadowLayer(SHADOW_LARGE_RADIUS, 0.0f, SHADOW_Y_OFFSET, SHADOW_LARGE_COLOUR);
-        }
-    }
-
     public void applyFromShortcutInfo(ShortcutInfo info, IconCache iconCache,
             boolean setDefaultPadding) {
         applyFromShortcutInfo(info, iconCache, setDefaultPadding, false);
@@ -328,7 +328,7 @@
         Drawable top = getCompoundDrawables()[1];
 
         if (top instanceof PreloadIconDrawable) {
-            ((PreloadIconDrawable) top).applyTheme(getPreloaderTheme());
+            ((PreloadIconDrawable) top).applyPreloaderTheme(getPreloaderTheme());
         }
         mSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
     }
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index 7424d61..e6865b2 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -2981,11 +2981,11 @@
     static boolean findVacantCell(int[] vacant, int spanX, int spanY,
             int xCount, int yCount, boolean[][] occupied) {
 
-        for (int y = 0; y < yCount; y++) {
-            for (int x = 0; x < xCount; x++) {
+        for (int y = 0; (y + spanY) <= yCount; y++) {
+            for (int x = 0; (x + spanX) <= xCount; x++) {
                 boolean available = !occupied[x][y];
-out:            for (int i = x; i < x + spanX - 1 && x < xCount; i++) {
-                    for (int j = y; j < y + spanY - 1 && y < yCount; j++) {
+out:            for (int i = x; i < x + spanX; i++) {
+                    for (int j = y; j < y + spanY; j++) {
                         available = available && !occupied[i][j];
                         if (!available) break out;
                     }
diff --git a/src/com/android/launcher3/DeleteDropTarget.java b/src/com/android/launcher3/DeleteDropTarget.java
index 5a5c002..ebe874f 100644
--- a/src/com/android/launcher3/DeleteDropTarget.java
+++ b/src/com/android/launcher3/DeleteDropTarget.java
@@ -19,6 +19,7 @@
 import android.animation.TimeInterpolator;
 import android.animation.ValueAnimator;
 import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.annotation.TargetApi;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.res.ColorStateList;
@@ -115,15 +116,6 @@
     private boolean isDragSourceWorkspaceOrFolder(DragObject d) {
         return (d.dragSource instanceof Workspace) || (d.dragSource instanceof Folder);
     }
-    private boolean isWorkspaceOrFolderApplication(DragObject d) {
-        return isDragSourceWorkspaceOrFolder(d) && (d.dragInfo instanceof ShortcutInfo);
-    }
-    private boolean isWorkspaceOrFolderWidget(DragObject d) {
-        return isDragSourceWorkspaceOrFolder(d) && (d.dragInfo instanceof LauncherAppWidgetInfo);
-    }
-    private boolean isWorkspaceFolder(DragObject d) {
-        return (d.dragSource instanceof Workspace) && (d.dragInfo instanceof FolderInfo);
-    }
 
     private void setHoverColor() {
         if (mCurrentDrawable != null) {
@@ -177,6 +169,7 @@
         return false;
     }
 
+    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
     @Override
     public void onDragStart(DragSource source, Object info, int dragAction) {
         boolean isVisible = true;
@@ -284,7 +277,8 @@
     }
 
     private boolean isUninstallFromWorkspace(DragObject d) {
-        if (LauncherAppState.isDisableAllApps() && isWorkspaceOrFolderApplication(d)) {
+        if (LauncherAppState.isDisableAllApps() && isDragSourceWorkspaceOrFolder(d)
+                && (d.dragInfo instanceof ShortcutInfo)) {
             ShortcutInfo shortcut = (ShortcutInfo) d.dragInfo;
             // Only allow manifest shortcuts to initiate an un-install.
             return !InstallShortcutReceiver.isValidShortcutLaunchIntent(shortcut.intent);
@@ -297,10 +291,7 @@
         boolean wasWaitingForUninstall = mWaitingForUninstall;
         mWaitingForUninstall = false;
         if (isAllAppsApplication(d.dragSource, item)) {
-            // Uninstall the application if it is being dragged from AppsCustomize
-            AppInfo appInfo = (AppInfo) item;
-            mLauncher.startApplicationUninstallActivity(appInfo.componentName, appInfo.flags,
-                    appInfo.user);
+            uninstallApp(mLauncher, (AppInfo) item);
         } else if (isUninstallFromWorkspace(d)) {
             ShortcutInfo shortcut = (ShortcutInfo) item;
             if (shortcut.intent != null && shortcut.intent.getComponent() != null) {
@@ -329,32 +320,8 @@
                     mLauncher.addOnResumeCallback(checkIfUninstallWasSuccess);
                 }
             }
-        } else if (isWorkspaceOrFolderApplication(d)) {
-            LauncherModel.deleteItemFromDatabase(mLauncher, item);
-        } else if (isWorkspaceFolder(d)) {
-            // Remove the folder from the workspace and delete the contents from launcher model
-            FolderInfo folderInfo = (FolderInfo) item;
-            mLauncher.removeFolder(folderInfo);
-            LauncherModel.deleteFolderContentsFromDatabase(mLauncher, folderInfo);
-        } else if (isWorkspaceOrFolderWidget(d)) {
-            // Remove the widget from the workspace
-            mLauncher.removeAppWidget((LauncherAppWidgetInfo) item);
-            LauncherModel.deleteItemFromDatabase(mLauncher, item);
-
-            final LauncherAppWidgetInfo launcherAppWidgetInfo = (LauncherAppWidgetInfo) item;
-            final LauncherAppWidgetHost appWidgetHost = mLauncher.getAppWidgetHost();
-
-            if (appWidgetHost != null && !launcherAppWidgetInfo.isCustomWidget()
-                    && launcherAppWidgetInfo.isWidgetIdValid()) {
-                // Deleting an app widget ID is a void call but writes to disk before returning
-                // to the caller...
-                new AsyncTask<Void, Void, Void>() {
-                    public Void doInBackground(Void ... args) {
-                        appWidgetHost.deleteAppWidgetId(launcherAppWidgetInfo.appWidgetId);
-                        return null;
-                    }
-                }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void) null);
-            }
+        } else if (isDragSourceWorkspaceOrFolder(d)) {
+            removeWorkspaceOrFolderItem(mLauncher, item, null);
         }
         if (wasWaitingForUninstall && !mWaitingForUninstall) {
             if (d.dragSource instanceof Folder) {
@@ -365,6 +332,52 @@
         }
     }
 
+    public static void uninstallApp(Launcher launcher, AppInfo info) {
+        launcher.startApplicationUninstallActivity(info.componentName, info.flags, info.user);
+    }
+
+    /**
+     * Removes the item from the workspace. If the view is not null, it also removes the view.
+     * @return true if the item was removed.
+     */
+    public static boolean removeWorkspaceOrFolderItem(Launcher launcher, ItemInfo item, View view) {
+        if (item instanceof ShortcutInfo) {
+            LauncherModel.deleteItemFromDatabase(launcher, item);
+        } else if (item instanceof FolderInfo) {
+            FolderInfo folder = (FolderInfo) item;
+            launcher.removeFolder(folder);
+            LauncherModel.deleteFolderContentsFromDatabase(launcher, folder);
+        } else if (item instanceof LauncherAppWidgetInfo) {
+            final LauncherAppWidgetInfo widget = (LauncherAppWidgetInfo) item;
+
+            // Remove the widget from the workspace
+            launcher.removeAppWidget(widget);
+            LauncherModel.deleteItemFromDatabase(launcher, widget);
+
+            final LauncherAppWidgetHost appWidgetHost = launcher.getAppWidgetHost();
+
+            if (appWidgetHost != null && !widget.isCustomWidget()
+                    && widget.isWidgetIdValid()) {
+                // Deleting an app widget ID is a void call but writes to disk before returning
+                // to the caller...
+                new AsyncTask<Void, Void, Void>() {
+                    public Void doInBackground(Void ... args) {
+                        appWidgetHost.deleteAppWidgetId(widget.appWidgetId);
+                        return null;
+                    }
+                }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void) null);
+            }
+        } else {
+            return false;
+        }
+
+        if (view != null) {
+            launcher.getWorkspace().removeWorkspaceItem(view);
+            launcher.getWorkspace().stripEmptyScreens();
+        }
+        return true;
+    }
+
     public void onDrop(DragObject d) {
         animateToTrashAndCompleteDrop(d);
     }
diff --git a/src/com/android/launcher3/FolderIcon.java b/src/com/android/launcher3/FolderIcon.java
index a359f11..a3e8295 100644
--- a/src/com/android/launcher3/FolderIcon.java
+++ b/src/com/android/launcher3/FolderIcon.java
@@ -127,6 +127,7 @@
 
     private void init() {
         mLongPressHelper = new CheckLongPressHelper(this);
+        setAccessibilityDelegate(LauncherAppState.getInstance().getAccessibilityDelegate());
     }
 
     public boolean isDropEnabled() {
diff --git a/src/com/android/launcher3/InfoDropTarget.java b/src/com/android/launcher3/InfoDropTarget.java
index 7e55af2..3c36361 100644
--- a/src/com/android/launcher3/InfoDropTarget.java
+++ b/src/com/android/launcher3/InfoDropTarget.java
@@ -76,28 +76,32 @@
         // acceptDrop is called just before onDrop. We do the work here, rather than
         // in onDrop, because it allows us to reject the drop (by returning false)
         // so that the object being dragged isn't removed from the drag source.
+
+        startDetailsActivityForInfo(d.dragInfo, mLauncher);
+        // There is no post-drop animation, so clean up the DragView now
+        d.deferDragViewCleanupPostAnimation = false;
+        return false;
+    }
+
+    public static void startDetailsActivityForInfo(Object info, Launcher launcher) {
         ComponentName componentName = null;
-        if (d.dragInfo instanceof AppInfo) {
-            componentName = ((AppInfo) d.dragInfo).componentName;
-        } else if (d.dragInfo instanceof ShortcutInfo) {
-            componentName = ((ShortcutInfo) d.dragInfo).intent.getComponent();
-        } else if (d.dragInfo instanceof PendingAddItemInfo) {
-            componentName = ((PendingAddItemInfo) d.dragInfo).componentName;
+        if (info instanceof AppInfo) {
+            componentName = ((AppInfo) info).componentName;
+        } else if (info instanceof ShortcutInfo) {
+            componentName = ((ShortcutInfo) info).intent.getComponent();
+        } else if (info instanceof PendingAddItemInfo) {
+            componentName = ((PendingAddItemInfo) info).componentName;
         }
         final UserHandleCompat user;
-        if (d.dragInfo instanceof ItemInfo) {
-            user = ((ItemInfo) d.dragInfo).user;
+        if (info instanceof ItemInfo) {
+            user = ((ItemInfo) info).user;
         } else {
             user = UserHandleCompat.myUserHandle();
         }
 
         if (componentName != null) {
-            mLauncher.startApplicationDetailsActivity(componentName, user);
+            launcher.startApplicationDetailsActivity(componentName, user);
         }
-
-        // There is no post-drop animation, so clean up the DragView now
-        d.deferDragViewCleanupPostAnimation = false;
-        return false;
     }
 
     @Override
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 0ceb862..5d8e136 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -162,7 +162,6 @@
     static final String EXTRA_SHORTCUT_DUPLICATE = "duplicate";
 
     static final int SCREEN_COUNT = 5;
-    static final int DEFAULT_SCREEN = 2;
 
     // To turn on these properties, type
     // adb shell setprop log.tag.PROPERTY_NAME [VERBOSE | SUPPRESS]
@@ -232,7 +231,6 @@
     private static final int ACTIVITY_START_DELAY = 1000;
 
     private static final Object sLock = new Object();
-    private static int sScreen = DEFAULT_SCREEN;
 
     private HashMap<Integer, Integer> mItemIdToViewId = new HashMap<Integer, Integer>();
     private static final AtomicInteger sNextGeneratedId = new AtomicInteger(1);
@@ -675,18 +673,7 @@
         return !mModel.isLoadingWorkspace();
     }
 
-    static int getScreen() {
-        synchronized (sLock) {
-            return sScreen;
-        }
-    }
-
-    static void setScreen(int screen) {
-        synchronized (sLock) {
-            sScreen = screen;
-        }
-    }
-
+    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
     public static int generateViewId() {
         if (Build.VERSION.SDK_INT >= 17) {
             return View.generateViewId();
@@ -1594,7 +1581,6 @@
      * Add a widget to the workspace.
      *
      * @param appWidgetId The app widget id
-     * @param cellInfo The position on screen where to create the widget.
      */
     private void completeAddAppWidget(int appWidgetId, long container, long screenId,
             AppWidgetHostView hostView, LauncherAppWidgetProviderInfo appWidgetInfo) {
@@ -2294,20 +2280,39 @@
         closeFolder();
         mWorkspace.moveToCustomContentScreen(animate);
     }
+
+    public void addPendingItem(PendingAddItemInfo info, long container, long screenId,
+            int[] cell, int spanX, int spanY) {
+        switch (info.itemType) {
+            case LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET:
+            case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
+                int span[] = new int[2];
+                span[0] = spanX;
+                span[1] = spanY;
+                addAppWidgetFromDrop((PendingAddWidgetInfo) info,
+                        container, screenId, cell, span);
+                break;
+            case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
+                processShortcutFromDrop(info.componentName, container, screenId, cell);
+                break;
+            default:
+                throw new IllegalStateException("Unknown item type: " + info.itemType);
+            }
+    }
+
     /**
      * Process a shortcut drop.
      *
      * @param componentName The name of the component
      * @param screenId The ID of the screen where it should be added
      * @param cell The cell it should be added to, optional
-     * @param position The location on the screen where it was dropped, optional
      */
-    void processShortcutFromDrop(ComponentName componentName, long container, long screenId,
-            int[] cell, int[] loc) {
+    private void processShortcutFromDrop(ComponentName componentName, long container, long screenId,
+            int[] cell) {
         resetAddInfo();
         mPendingAddInfo.container = container;
         mPendingAddInfo.screenId = screenId;
-        mPendingAddInfo.dropPos = loc;
+        mPendingAddInfo.dropPos = null;
 
         if (cell != null) {
             mPendingAddInfo.cellX = cell[0];
@@ -2325,14 +2330,13 @@
      * @param info The PendingAppWidgetInfo of the widget being added.
      * @param screenId The ID of the screen where it should be added
      * @param cell The cell it should be added to, optional
-     * @param position The location on the screen where it was dropped, optional
      */
-    void addAppWidgetFromDrop(PendingAddWidgetInfo info, long container, long screenId,
-            int[] cell, int[] span, int[] loc) {
+    private void addAppWidgetFromDrop(PendingAddWidgetInfo info, long container, long screenId,
+            int[] cell, int[] span) {
         resetAddInfo();
         mPendingAddInfo.container = info.container = container;
         mPendingAddInfo.screenId = info.screenId = screenId;
-        mPendingAddInfo.dropPos = loc;
+        mPendingAddInfo.dropPos = null;
         mPendingAddInfo.minSpanX = info.minSpanX;
         mPendingAddInfo.minSpanY = info.minSpanY;
 
@@ -3291,6 +3295,7 @@
         showAppsCustomizeHelper(animated, springLoaded, contentType);
     }
 
+    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
     private void showAppsCustomizeHelper(final boolean animated, final boolean springLoaded,
                                          final AppsCustomizePagedView.ContentType contentType) {
         if (mStateAnimation != null) {
@@ -3303,13 +3308,10 @@
 
         final Resources res = getResources();
 
-        final int duration = res.getInteger(R.integer.config_appsCustomizeZoomInTime);
-        final int fadeDuration = res.getInteger(R.integer.config_appsCustomizeFadeInTime);
         final int revealDuration = res.getInteger(R.integer.config_appsCustomizeRevealTime);
         final int itemsAlphaStagger =
                 res.getInteger(R.integer.config_appsCustomizeItemsAlphaStagger);
 
-        final float scale = (float) res.getInteger(R.integer.config_appsCustomizeZoomScaleFactor);
         final View fromView = mWorkspace;
         final AppsCustomizeTabHost toView = mAppsCustomizeTabHost;
 
@@ -3536,14 +3538,10 @@
         boolean material = Utilities.isLmpOrAbove();
         Resources res = getResources();
 
-        final int duration = res.getInteger(R.integer.config_appsCustomizeZoomOutTime);
-        final int fadeOutDuration = res.getInteger(R.integer.config_appsCustomizeFadeOutTime);
         final int revealDuration = res.getInteger(R.integer.config_appsCustomizeConcealTime);
         final int itemsAlphaStagger =
                 res.getInteger(R.integer.config_appsCustomizeItemsAlphaStagger);
 
-        final float scaleFactor = (float)
-                res.getInteger(R.integer.config_appsCustomizeZoomScaleFactor);
         final View fromView = mAppsCustomizeTabHost;
         final View toView = mWorkspace;
         Animator workspaceAnim = null;
@@ -4139,6 +4137,19 @@
         }
     }
 
+    @Override
+    public void bindAddPendingItem(final PendingAddItemInfo info, final long container,
+            final long screenId, final int[] cell, final int spanX, final int spanY) {
+        showWorkspace(true, new Runnable() {
+
+            @Override
+            public void run() {
+                mWorkspace.snapToPage(mWorkspace.getPageIndexForScreenId(screenId));
+                addPendingItem(info, container, screenId, cell, spanX, spanY);
+            }
+        });
+    }
+
     private boolean shouldShowWeightWatcher() {
         String spKey = LauncherAppState.getSharedPreferencesKey();
         SharedPreferences sp = getSharedPreferences(spKey, Context.MODE_PRIVATE);
diff --git a/src/com/android/launcher3/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/LauncherAccessibilityDelegate.java
new file mode 100644
index 0000000..c9e277e
--- /dev/null
+++ b/src/com/android/launcher3/LauncherAccessibilityDelegate.java
@@ -0,0 +1,109 @@
+package com.android.launcher3;
+
+import android.annotation.TargetApi;
+import android.os.Build;
+import android.os.Bundle;
+import android.util.SparseArray;
+import android.view.View;
+import android.view.View.AccessibilityDelegate;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
+
+import com.android.launcher3.LauncherModel.ScreenPosProvider;
+
+import java.util.ArrayList;
+
+@TargetApi(Build.VERSION_CODES.LOLLIPOP)
+public class LauncherAccessibilityDelegate extends AccessibilityDelegate {
+
+    public static final int REMOVE = R.id.action_remove;
+    public static final int INFO = R.id.action_info;
+    public static final int UNINSTALL = R.id.action_uninstall;
+    public static final int ADD_TO_WORKSPACE = R.id.action_add_to_workspace;
+
+    private final SparseArray<AccessibilityAction> mActions =
+            new SparseArray<AccessibilityAction>();
+    private final Launcher mLauncher;
+
+    public LauncherAccessibilityDelegate(Launcher launcher) {
+        mLauncher = launcher;
+
+        mActions.put(REMOVE, new AccessibilityAction(REMOVE,
+                launcher.getText(R.string.delete_target_label)));
+        mActions.put(INFO, new AccessibilityAction(INFO,
+                launcher.getText(R.string.info_target_label)));
+        mActions.put(UNINSTALL, new AccessibilityAction(UNINSTALL,
+                launcher.getText(R.string.delete_target_uninstall_label)));
+        mActions.put(ADD_TO_WORKSPACE, new AccessibilityAction(ADD_TO_WORKSPACE,
+                launcher.getText(R.string.action_add_to_workspace)));
+    }
+
+    @Override
+    public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
+        super.onInitializeAccessibilityNodeInfo(host, info);
+        if (!(host.getTag() instanceof ItemInfo)) return;
+        ItemInfo item = (ItemInfo) host.getTag();
+
+        if ((item instanceof ShortcutInfo)
+                || (item instanceof LauncherAppWidgetInfo)
+                || (item instanceof FolderInfo)) {
+            // Workspace shortcut / widget
+            info.addAction(mActions.get(REMOVE));
+        } else if ((item instanceof AppInfo) || (item instanceof PendingAddItemInfo)) {
+            // App or Widget from customization tray
+            if (item instanceof AppInfo) {
+                info.addAction(mActions.get(UNINSTALL));
+            }
+            info.addAction(mActions.get(INFO));
+            info.addAction(mActions.get(ADD_TO_WORKSPACE));
+        }
+    }
+
+    @Override
+    public boolean performAccessibilityAction(View host, int action, Bundle args) {
+        if ((host.getTag() instanceof ItemInfo)
+                && performAction(host, (ItemInfo) host.getTag(), action)) {
+            return true;
+        }
+        return super.performAccessibilityAction(host, action, args);
+    }
+
+    public boolean performAction(View host, ItemInfo item, int action) {
+        if (action == REMOVE) {
+            return DeleteDropTarget.removeWorkspaceOrFolderItem(mLauncher, item, host);
+        } else if (action == INFO) {
+            InfoDropTarget.startDetailsActivityForInfo(item, mLauncher);
+            return true;
+        } else if (action == UNINSTALL) {
+            DeleteDropTarget.uninstallApp(mLauncher, (AppInfo) item);
+            return true;
+        } else if (action == ADD_TO_WORKSPACE) {
+            final int preferredPage = mLauncher.getWorkspace().getCurrentPage();
+            final ScreenPosProvider screenProvider = new ScreenPosProvider() {
+
+                @Override
+                public int getScreenIndex(ArrayList<Long> screenIDs) {
+                    return preferredPage;
+                }
+            };
+            if (item instanceof AppInfo) {
+                final ArrayList<ItemInfo> addShortcuts = new ArrayList<ItemInfo>();
+                addShortcuts.add(((AppInfo) item).makeShortcut());
+                mLauncher.showWorkspace(true, new Runnable() {
+
+                    @Override
+                    public void run() {
+                        mLauncher.getModel().addAndBindAddedWorkspaceApps(
+                                mLauncher, addShortcuts, screenProvider, 0, true);
+                    }
+                });
+                return true;
+            } else if (item instanceof PendingAddItemInfo) {
+                mLauncher.getModel().addAndBindPendingItem(
+                        mLauncher, (PendingAddItemInfo) item, screenProvider, 0);
+                return true;
+            }
+        }
+        return false;
+    }
+}
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index b7c45a3..87e9aae 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -32,10 +32,13 @@
 import android.util.DisplayMetrics;
 import android.util.Log;
 import android.view.Display;
+import android.view.View.AccessibilityDelegate;
 import android.view.WindowManager;
+
 import com.android.launcher3.compat.LauncherAppsCompat;
 import com.android.launcher3.compat.PackageInstallerCompat;
 import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo;
+
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 
@@ -62,6 +65,7 @@
     private static LauncherAppState INSTANCE;
 
     private DynamicGrid mDynamicGrid;
+    private AccessibilityDelegate mAccessibilityDelegate;
 
     public static LauncherAppState getInstance() {
         if (INSTANCE == null) {
@@ -162,9 +166,15 @@
 
     LauncherModel setLauncher(Launcher launcher) {
         mModel.initialize(launcher);
+        mAccessibilityDelegate = ((launcher != null) && Utilities.isLmpOrAbove()) ?
+            new LauncherAccessibilityDelegate(launcher) : null;
         return mModel;
     }
 
+    AccessibilityDelegate getAccessibilityDelegate() {
+        return mAccessibilityDelegate;
+    }
+
     public IconCache getIconCache() {
         return mIconCache;
     }
diff --git a/src/com/android/launcher3/LauncherAppWidgetHostView.java b/src/com/android/launcher3/LauncherAppWidgetHostView.java
index ebe55ab..2d04df2 100644
--- a/src/com/android/launcher3/LauncherAppWidgetHostView.java
+++ b/src/com/android/launcher3/LauncherAppWidgetHostView.java
@@ -48,6 +48,7 @@
         mLongPressHelper = new CheckLongPressHelper(this);
         mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
         mDragLayer = ((Launcher) context).getDragLayer();
+        setAccessibilityDelegate(LauncherAppState.getInstance().getAccessibilityDelegate());
     }
 
     @Override
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index 95ebaec..7b5f846 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -29,7 +29,6 @@
 import android.content.Intent.ShortcutIconResource;
 import android.content.IntentFilter;
 import android.content.SharedPreferences;
-import android.content.pm.LauncherApps.Callback;
 import android.content.pm.PackageManager;
 import android.content.pm.ProviderInfo;
 import android.content.pm.ResolveInfo;
@@ -38,6 +37,7 @@
 import android.database.Cursor;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
+import android.graphics.Rect;
 import android.net.Uri;
 import android.os.Environment;
 import android.os.Handler;
@@ -49,6 +49,7 @@
 import android.provider.BaseColumns;
 import android.text.TextUtils;
 import android.util.Log;
+import android.util.LongSparseArray;
 import android.util.Pair;
 
 import com.android.launcher3.compat.AppWidgetManagerCompat;
@@ -108,7 +109,6 @@
     private DeferredHandler mHandler = new DeferredHandler();
     private LoaderTask mLoaderTask;
     private boolean mIsLoaderTaskRunning;
-    private volatile boolean mFlushingWorkerThread;
 
     /**
      * Maintain a set of packages per user, for which we added a shortcut on the workspace.
@@ -219,12 +219,18 @@
         public boolean isAllAppsButtonRank(int rank);
         public void onPageBoundSynchronously(int page);
         public void dumpLogsToLocalData();
+        public void bindAddPendingItem(PendingAddItemInfo info, long container, long screenId,
+                int[] cell, int spanX, int spanY);
     }
 
     public interface ItemInfoFilter {
         public boolean filterItem(ItemInfo parent, ItemInfo info, ComponentName cn);
     }
 
+    public interface ScreenPosProvider {
+        int getScreenIndex(ArrayList<Long> screenIDs);
+    }
+
     LauncherModel(LauncherAppState app, IconCache iconCache, AppFilter appFilter) {
         Context context = app.getContext();
 
@@ -287,67 +293,6 @@
         return mOldContentProviderExists && !launcher.isLauncherPreinstalled() ;
     }
 
-    static boolean findNextAvailableIconSpaceInScreen(ArrayList<ItemInfo> items, int[] xy,
-                                 long screen) {
-        LauncherAppState app = LauncherAppState.getInstance();
-        DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
-        final int xCount = (int) grid.numColumns;
-        final int yCount = (int) grid.numRows;
-        boolean[][] occupied = new boolean[xCount][yCount];
-
-        int cellX, cellY, spanX, spanY;
-        for (int i = 0; i < items.size(); ++i) {
-            final ItemInfo item = items.get(i);
-            if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
-                if (item.screenId == screen) {
-                    cellX = item.cellX;
-                    cellY = item.cellY;
-                    spanX = item.spanX;
-                    spanY = item.spanY;
-                    for (int x = cellX; 0 <= x && x < cellX + spanX && x < xCount; x++) {
-                        for (int y = cellY; 0 <= y && y < cellY + spanY && y < yCount; y++) {
-                            occupied[x][y] = true;
-                        }
-                    }
-                }
-            }
-        }
-
-        return CellLayout.findVacantCell(xy, 1, 1, xCount, yCount, occupied);
-    }
-    static Pair<Long, int[]> findNextAvailableIconSpace(Context context, String name,
-                                                        Intent launchIntent,
-                                                        int firstScreenIndex,
-                                                        ArrayList<Long> workspaceScreens) {
-        // Lock on the app so that we don't try and get the items while apps are being added
-        LauncherAppState app = LauncherAppState.getInstance();
-        LauncherModel model = app.getModel();
-        boolean found = false;
-        synchronized (app) {
-            if (sWorkerThread.getThreadId() != Process.myTid()) {
-                // Flush the LauncherModel worker thread, so that if we just did another
-                // processInstallShortcut, we give it time for its shortcut to get added to the
-                // database (getItemsInLocalCoordinates reads the database)
-                model.flushWorkerThread();
-            }
-            final ArrayList<ItemInfo> items = LauncherModel.getItemsInLocalCoordinates(context);
-
-            // Try adding to the workspace screens incrementally, starting at the default or center
-            // screen and alternating between +1, -1, +2, -2, etc. (using ~ ceil(i/2f)*(-1)^(i-1))
-            firstScreenIndex = Math.min(firstScreenIndex, workspaceScreens.size());
-            int count = workspaceScreens.size();
-            for (int screen = firstScreenIndex; screen < count && !found; screen++) {
-                int[] tmpCoordinates = new int[2];
-                if (findNextAvailableIconSpaceInScreen(items, tmpCoordinates,
-                        workspaceScreens.get(screen))) {
-                    // Update the Launcher db
-                    return new Pair<Long, int[]>(workspaceScreens.get(screen), tmpCoordinates);
-                }
-            }
-        }
-        return null;
-    }
-
     public void setPackageState(final ArrayList<PackageInstallInfo> installInfo) {
         // Process the updated package state
         Runnable r = new Runnable() {
@@ -402,11 +347,196 @@
 
     public void addAndBindAddedWorkspaceApps(final Context context,
             final ArrayList<ItemInfo> workspaceApps) {
-        final Callbacks callbacks = getCallback();
+        addAndBindAddedWorkspaceApps(context, workspaceApps,
+                new ScreenPosProvider() {
 
-        if (workspaceApps == null) {
-            throw new RuntimeException("workspaceApps and allAppsApps must not be null");
+                    @Override
+                    public int getScreenIndex(ArrayList<Long> screenIDs) {
+                        return screenIDs.isEmpty() ? 0 : 1;
+                    }
+                }, 1, false);
+    }
+
+    private static boolean findNextAvailableIconSpaceInScreen(ArrayList<Rect> occupiedPos,
+            int[] xy, int spanX, int spanY) {
+        LauncherAppState app = LauncherAppState.getInstance();
+        DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
+        final int xCount = (int) grid.numColumns;
+        final int yCount = (int) grid.numRows;
+        boolean[][] occupied = new boolean[xCount][yCount];
+        if (occupiedPos != null) {
+            for (Rect r : occupiedPos) {
+                for (int x = r.left; 0 <= x && x < r.right && x < xCount; x++) {
+                    for (int y = r.top; 0 <= y && y < r.bottom && y < yCount; y++) {
+                        occupied[x][y] = true;
+                    }
+                }
+            }
         }
+        return CellLayout.findVacantCell(xy, spanX, spanY, xCount, yCount, occupied);
+    }
+
+    /**
+     * Find a position on the screen for the given size or adds a new screen.
+     * @return screenId and the coordinates for the item.
+     */
+    private static Pair<Long, int[]> findSpaceForItem(
+            Context context,
+            ScreenPosProvider preferredScreen,
+            int fallbackStartScreen,
+            ArrayList<Long> workspaceScreens,
+            ArrayList<Long> addedWorkspaceScreensFinal,
+            int spanX, int spanY) {
+        // Load position of items which are on the desktop. We can't use sBgItemsIdMap because
+        // loadWorkspace() may not have been called.
+        final ContentResolver cr = context.getContentResolver();
+        Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI,
+                new String[] {
+                    LauncherSettings.Favorites.SCREEN,
+                    LauncherSettings.Favorites.CELLX,
+                    LauncherSettings.Favorites.CELLY,
+                    LauncherSettings.Favorites.SPANX,
+                    LauncherSettings.Favorites.SPANY,
+                    LauncherSettings.Favorites.CONTAINER
+                 },
+                 "container=?",
+                 new String[] { Integer.toString(LauncherSettings.Favorites.CONTAINER_DESKTOP) },
+                 null);
+
+        final int screenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
+        final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
+        final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
+        final int spanXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANX);
+        final int spanYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANY);
+        LongSparseArray<ArrayList<Rect>> screenItems = new LongSparseArray<ArrayList<Rect>>();
+        try {
+            while (c.moveToNext()) {
+                Rect rect = new Rect();
+                rect.left = c.getInt(cellXIndex);
+                rect.top = c.getInt(cellYIndex);
+                rect.right = rect.left + Math.max(1, c.getInt(spanXIndex));
+                rect.bottom = rect.top + Math.max(1, c.getInt(spanYIndex));
+
+                long screenId = c.getInt(screenIndex);
+                ArrayList<Rect> items = screenItems.get(screenId);
+                if (items == null) {
+                    items = new ArrayList<Rect>();
+                    screenItems.put(screenId, items);
+                }
+                items.add(rect);
+            }
+        } catch (Exception e) {
+            screenItems.clear();
+        } finally {
+            c.close();
+        }
+
+        // Find appropriate space for the item.
+        long screenId = 0;
+        int[] cordinates = new int[2];
+        boolean found = false;
+
+        int screenCount = workspaceScreens.size();
+        // First check the preferred screen.
+        int preferredScreenIndex = preferredScreen.getScreenIndex(workspaceScreens);
+        if (preferredScreenIndex < screenCount) {
+            screenId = workspaceScreens.get(preferredScreenIndex);
+            found = findNextAvailableIconSpaceInScreen(
+                    screenItems.get(screenId), cordinates, spanX, spanY);
+        }
+
+        if (!found) {
+            // Search on any of the screens.
+            for (int screen = fallbackStartScreen; screen < screenCount; screen++) {
+                screenId = workspaceScreens.get(screen);
+                if (findNextAvailableIconSpaceInScreen(
+                        screenItems.get(screenId), cordinates, spanX, spanY)) {
+                    // We found a space for it
+                    found = true;
+                    break;
+                }
+            }
+        }
+
+        if (!found) {
+            // Still no position found. Add a new screen to the end.
+            screenId = LauncherAppState.getLauncherProvider().generateNewScreenId();
+
+            // Save the screen id for binding in the workspace
+            workspaceScreens.add(screenId);
+            addedWorkspaceScreensFinal.add(screenId);
+
+            // If we still can't find an empty space, then God help us all!!!
+            if (!findNextAvailableIconSpaceInScreen(
+                    screenItems.get(screenId), cordinates, spanX, spanY)) {
+                throw new RuntimeException("Can't find space to add the item");
+            }
+        }
+        return Pair.create(screenId, cordinates);
+    }
+
+    /**
+     * Adds the provided items to the workspace.
+     * @param preferredScreen the screen where we should try to add the app first
+     * @param fallbackStartScreen the screen to start search for empty space if
+     * preferredScreen is not available.
+     */
+    public void addAndBindPendingItem(
+            final Context context,
+            final PendingAddItemInfo addInfo,
+            final ScreenPosProvider preferredScreen,
+            final int fallbackStartScreen) {
+        final Callbacks callbacks = getCallback();
+        // Process the newly added applications and add them to the database first
+        Runnable r = new Runnable() {
+            public void run() {
+                final ArrayList<Long> addedWorkspaceScreensFinal = new ArrayList<Long>();
+
+                ArrayList<Long> workspaceScreens = new ArrayList<Long>();
+                TreeMap<Integer, Long> orderedScreens = loadWorkspaceScreensDb(context);
+                for (Integer i : orderedScreens.keySet()) {
+                    long screenId = orderedScreens.get(i);
+                    workspaceScreens.add(screenId);
+                }
+
+                // Find appropriate space for the item.
+                Pair<Long, int[]> coords = findSpaceForItem(context, preferredScreen,
+                        fallbackStartScreen, workspaceScreens, addedWorkspaceScreensFinal,
+                        addInfo.spanX,
+                        addInfo.spanY);
+                final long screenId = coords.first;
+                final int[] cordinates = coords.second;
+
+                // Update the workspace screens
+                updateWorkspaceScreenOrder(context, workspaceScreens);
+                runOnMainThread(new Runnable() {
+                    public void run() {
+                        Callbacks cb = getCallback();
+                        if (callbacks == cb && cb != null) {
+                            cb.bindAddScreens(addedWorkspaceScreensFinal);
+                            cb.bindAddPendingItem(addInfo,
+                                    LauncherSettings.Favorites.CONTAINER_DESKTOP,
+                                    screenId, cordinates, addInfo.spanX, addInfo.spanY);
+                        }
+                    }
+                });
+            }
+        };
+        runOnWorkerThread(r);
+    }
+
+    /**
+     * Adds the provided items to the workspace.
+     * @param preferredScreen the screen where we should try to add the app first
+     * @param fallbackStartScreen the screen to start search for empty space if
+     * preferredScreen is not available.
+     */
+    public void addAndBindAddedWorkspaceApps(final Context context,
+            final ArrayList<ItemInfo> workspaceApps,
+            final ScreenPosProvider preferredScreen,
+            final int fallbackStartScreen,
+            final boolean allowDuplicate) {
+        final Callbacks callbacks = getCallback();
         if (workspaceApps.isEmpty()) {
             return;
         }
@@ -427,53 +557,27 @@
                 }
 
                 synchronized(sBgLock) {
-                    Iterator<ItemInfo> iter = workspaceApps.iterator();
-                    while (iter.hasNext()) {
-                        ItemInfo a = iter.next();
-                        final String name = a.title.toString();
-                        final Intent launchIntent = a.getIntent();
-
-                        // Short-circuit this logic if the icon exists somewhere on the workspace
-                        if (shortcutExists(context, name, launchIntent, a.user)) {
-                            continue;
-                        }
-
-                        // Add this icon to the db, creating a new page if necessary.  If there
-                        // is only the empty page then we just add items to the first page.
-                        // Otherwise, we add them to the next pages.
-                        int startSearchPageIndex = workspaceScreens.isEmpty() ? 0 : 1;
-                        Pair<Long, int[]> coords = LauncherModel.findNextAvailableIconSpace(context,
-                                name, launchIntent, startSearchPageIndex, workspaceScreens);
-                        if (coords == null) {
-                            LauncherProvider lp = LauncherAppState.getLauncherProvider();
-
-                            // If we can't find a valid position, then just add a new screen.
-                            // This takes time so we need to re-queue the add until the new
-                            // page is added.  Create as many screens as necessary to satisfy
-                            // the startSearchPageIndex.
-                            int numPagesToAdd = Math.max(1, startSearchPageIndex + 1 -
-                                    workspaceScreens.size());
-                            while (numPagesToAdd > 0) {
-                                long screenId = lp.generateNewScreenId();
-                                // Save the screen id for binding in the workspace
-                                workspaceScreens.add(screenId);
-                                addedWorkspaceScreensFinal.add(screenId);
-                                numPagesToAdd--;
+                    for (ItemInfo item : workspaceApps) {
+                        if (!allowDuplicate) {
+                            // Short-circuit this logic if the icon exists somewhere on the workspace
+                            if (shortcutExists(context, item.title.toString(),
+                                    item.getIntent(), item.user)) {
+                                continue;
                             }
+                        }
 
-                            // Find the coordinate again
-                            coords = LauncherModel.findNextAvailableIconSpace(context,
-                                    name, launchIntent, startSearchPageIndex, workspaceScreens);
-                        }
-                        if (coords == null) {
-                            throw new RuntimeException("Coordinates should not be null");
-                        }
+                        // Find appropriate space for the item.
+                        Pair<Long, int[]> coords = findSpaceForItem(context, preferredScreen,
+                                fallbackStartScreen, workspaceScreens, addedWorkspaceScreensFinal,
+                                1, 1);
+                        long screenId = coords.first;
+                        int[] cordinates = coords.second;
 
                         ShortcutInfo shortcutInfo;
-                        if (a instanceof ShortcutInfo) {
-                            shortcutInfo = (ShortcutInfo) a;
-                        } else if (a instanceof AppInfo) {
-                            shortcutInfo = ((AppInfo) a).makeShortcut();
+                        if (item instanceof ShortcutInfo) {
+                            shortcutInfo = (ShortcutInfo) item;
+                        } else if (item instanceof AppInfo) {
+                            shortcutInfo = ((AppInfo) item).makeShortcut();
                         } else {
                             throw new RuntimeException("Unexpected info type");
                         }
@@ -481,7 +585,7 @@
                         // Add the shortcut to the db
                         addItemToDatabase(context, shortcutInfo,
                                 LauncherSettings.Favorites.CONTAINER_DESKTOP,
-                                coords.first, coords.second[0], coords.second[1], false);
+                                screenId, cordinates[0], cordinates[1], false);
                         // Save the ShortcutInfo for binding in the workspace
                         addedShortcutsFinal.add(shortcutInfo);
                     }
@@ -717,35 +821,6 @@
         }
     }
 
-    public void flushWorkerThread() {
-        mFlushingWorkerThread = true;
-        Runnable waiter = new Runnable() {
-                public void run() {
-                    synchronized (this) {
-                        notifyAll();
-                        mFlushingWorkerThread = false;
-                    }
-                }
-            };
-
-        synchronized(waiter) {
-            runOnWorkerThread(waiter);
-            if (mLoaderTask != null) {
-                synchronized(mLoaderTask) {
-                    mLoaderTask.notify();
-                }
-            }
-            boolean success = false;
-            while (!success) {
-                try {
-                    waiter.wait();
-                    success = true;
-                } catch (InterruptedException e) {
-                }
-            }
-        }
-    }
-
     /**
      * Move an item in the DB to a new <container, screen, cellX, cellY>
      */
@@ -890,57 +965,6 @@
     }
 
     /**
-     * Returns an ItemInfo array containing all the items in the LauncherModel.
-     * The ItemInfo.id is not set through this function.
-     */
-    static ArrayList<ItemInfo> getItemsInLocalCoordinates(Context context) {
-        ArrayList<ItemInfo> items = new ArrayList<ItemInfo>();
-        final ContentResolver cr = context.getContentResolver();
-        Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI, new String[] {
-                LauncherSettings.Favorites.ITEM_TYPE, LauncherSettings.Favorites.CONTAINER,
-                LauncherSettings.Favorites.SCREEN,
-                LauncherSettings.Favorites.CELLX, LauncherSettings.Favorites.CELLY,
-                LauncherSettings.Favorites.SPANX, LauncherSettings.Favorites.SPANY,
-                LauncherSettings.Favorites.PROFILE_ID }, null, null, null);
-
-        final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
-        final int containerIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER);
-        final int screenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
-        final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
-        final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
-        final int rankIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.RANK);
-        final int spanXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANX);
-        final int spanYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANY);
-        final int profileIdIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.PROFILE_ID);
-        UserManagerCompat userManager = UserManagerCompat.getInstance(context);
-        try {
-            while (c.moveToNext()) {
-                ItemInfo item = new ItemInfo();
-                item.cellX = c.getInt(cellXIndex);
-                item.cellY = c.getInt(cellYIndex);
-                item.rank = c.getInt(rankIndex);
-                item.spanX = Math.max(1, c.getInt(spanXIndex));
-                item.spanY = Math.max(1, c.getInt(spanYIndex));
-                item.container = c.getInt(containerIndex);
-                item.itemType = c.getInt(itemTypeIndex);
-                item.screenId = c.getInt(screenIndex);
-                long serialNumber = c.getInt(profileIdIndex);
-                item.user = userManager.getUserForSerialNumber(serialNumber);
-                // Skip if user has been deleted.
-                if (item.user != null) {
-                    items.add(item);
-                }
-            }
-        } catch (Exception e) {
-            items.clear();
-        } finally {
-            c.close();
-        }
-
-        return items;
-    }
-
-    /**
      * Find a folder in the db, creating the FolderInfo if necessary, and adding it to folderList.
      */
     FolderInfo getFolderById(Context context, HashMap<Long,FolderInfo> folderList, long id) {
@@ -1543,7 +1567,7 @@
                         }
                     });
 
-                while (!mStopped && !mLoadAndBindStepFinished && !mFlushingWorkerThread) {
+                while (!mStopped && !mLoadAndBindStepFinished) {
                     try {
                         // Just in case mFlushingWorkerThread changes but we aren't woken up,
                         // wait no longer than 1sec at a time
diff --git a/src/com/android/launcher3/PagedViewWidget.java b/src/com/android/launcher3/PagedViewWidget.java
index 41270e3..107069b 100644
--- a/src/com/android/launcher3/PagedViewWidget.java
+++ b/src/com/android/launcher3/PagedViewWidget.java
@@ -16,7 +16,6 @@
 
 package com.android.launcher3;
 
-import android.appwidget.AppWidgetProviderInfo;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
@@ -67,6 +66,7 @@
 
         setWillNotDraw(false);
         setClipToPadding(false);
+        setAccessibilityDelegate(LauncherAppState.getInstance().getAccessibilityDelegate());
     }
 
     @Override
diff --git a/src/com/android/launcher3/PreloadIconDrawable.java b/src/com/android/launcher3/PreloadIconDrawable.java
index 2972c4f..bcb59c4 100644
--- a/src/com/android/launcher3/PreloadIconDrawable.java
+++ b/src/com/android/launcher3/PreloadIconDrawable.java
@@ -54,12 +54,11 @@
         mPaint.setStrokeCap(Paint.Cap.ROUND);
 
         setBounds(icon.getBounds());
-        applyTheme(theme);
+        applyPreloaderTheme(theme);
         onLevelChange(0);
     }
 
-    @Override
-    public void applyTheme(Theme t) {
+    public void applyPreloaderTheme(Theme t) {
         TypedArray ta = t.obtainStyledAttributes(R.styleable.PreloadIconDrawable);
         mBgDrawable = ta.getDrawable(R.styleable.PreloadIconDrawable_background);
         mBgDrawable.setFilterBitmap(true);
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index b6e85f3..44d7757 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -31,10 +31,7 @@
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.ComponentName;
 import android.content.Context;
-import android.content.Intent;
 import android.content.SharedPreferences;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.graphics.Bitmap;
@@ -77,8 +74,6 @@
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
-import java.util.Map;
-import java.util.Set;
 import java.util.concurrent.atomic.AtomicInteger;
 
 /**
@@ -92,9 +87,6 @@
         Insettable {
     private static final String TAG = "Launcher.Workspace";
 
-    // Y rotation to apply to the workspace screens
-    private static final float WORKSPACE_OVERSCROLL_ROTATION = 24f;
-
     private static final int CHILDREN_OUTLINE_FADE_OUT_DELAY = 0;
     private static final int CHILDREN_OUTLINE_FADE_OUT_DURATION = 375;
     private static final int CHILDREN_OUTLINE_FADE_IN_DURATION = 100;
@@ -228,7 +220,6 @@
     private Runnable mDelayedResizeRunnable;
     private Runnable mDelayedSnapToPageRunnable;
     private Point mDisplaySize = new Point();
-    private int mCameraDistance;
 
     // Variables relating to the creation of user folders by hovering shortcuts over shortcuts
     private static final int FOLDER_CREATION_TIMEOUT = 0;
@@ -346,7 +337,6 @@
         mSpringLoadedShrinkFactor =
             res.getInteger(R.integer.config_workspaceSpringLoadShrinkPercentage) / 100.0f;
         mOverviewModeShrinkFactor = grid.getOverviewModeScale();
-        mCameraDistance = res.getInteger(R.integer.config_cameraDistance);
         mOriginalDefaultPage = mDefaultPage = a.getInt(R.styleable.Workspace_defaultScreen, 1);
         a.recycle();
 
@@ -450,7 +440,6 @@
      */
     protected void initWorkspace() {
         mCurrentPage = mDefaultPage;
-        Launcher.setScreen(mCurrentPage);
         LauncherAppState app = LauncherAppState.getInstance();
         DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
         mIconCache = app.getIconCache();
@@ -1323,7 +1312,6 @@
     @Override
     protected void notifyPageSwitchListener() {
         super.notifyPageSwitchListener();
-        Launcher.setScreen(getNextPage());
 
         if (hasCustomContent() && getNextPage() == 0 && !mCustomContentShowing) {
             mCustomContentShowing = true;
@@ -3952,23 +3940,8 @@
 
                     // When dragging and dropping from customization tray, we deal with creating
                     // widgets/shortcuts/folders in a slightly different way
-                    switch (pendingInfo.itemType) {
-                    case LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET:
-                    case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
-                        int span[] = new int[2];
-                        span[0] = item.spanX;
-                        span[1] = item.spanY;
-                        mLauncher.addAppWidgetFromDrop((PendingAddWidgetInfo) pendingInfo,
-                                container, screenId, mTargetCell, span, null);
-                        break;
-                    case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
-                        mLauncher.processShortcutFromDrop(pendingInfo.componentName,
-                                container, screenId, mTargetCell, null);
-                        break;
-                    default:
-                        throw new IllegalStateException("Unknown item type: " +
-                                pendingInfo.itemType);
-                    }
+                    mLauncher.addPendingItem(pendingInfo, container, screenId, mTargetCell,
+                            item.spanX, item.spanY);
                 }
             };
             boolean isWidget = pendingInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET
@@ -3998,7 +3971,7 @@
             case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
                 if (info.container == NO_ID && info instanceof AppInfo) {
                     // Came from all apps -- make a copy
-                    info = new ShortcutInfo((AppInfo) info);
+                    info = ((AppInfo) info).makeShortcut();
                 }
                 view = mLauncher.createShortcut(R.layout.application, cellLayout,
                         (ShortcutInfo) info);
@@ -4250,15 +4223,7 @@
 
         if (success && !(beingCalledAfterUninstall && !mUninstallSuccessful)) {
             if (target != this && mDragInfo != null) {
-                CellLayout parentCell = getParentCellLayoutForView(mDragInfo.cell);
-                if (parentCell != null) {
-                    parentCell.removeView(mDragInfo.cell);
-                } else if (LauncherAppState.isDogfoodBuild()) {
-                    throw new NullPointerException("mDragInfo.cell has null parent");
-                }
-                if (mDragInfo.cell instanceof DropTarget) {
-                    mDragController.removeDropTarget((DropTarget) mDragInfo.cell);
-                }
+                removeWorkspaceItem(mDragInfo.cell);
             }
         } else if (mDragInfo != null) {
             CellLayout cellLayout;
@@ -4283,6 +4248,18 @@
         mDragInfo = null;
     }
 
+    public void removeWorkspaceItem(View v) {
+        CellLayout parentCell = getParentCellLayoutForView(v);
+        if (parentCell != null) {
+            parentCell.removeView(v);
+        } else if (LauncherAppState.isDogfoodBuild()) {
+            throw new NullPointerException("mDragInfo.cell has null parent");
+        }
+        if (v instanceof DropTarget) {
+            mDragController.removeDropTarget((DropTarget) v);
+        }
+    }
+
     public void deferCompleteDropAfterUninstallActivity() {
         mDeferDropAfterUninstall = true;
     }
@@ -4478,12 +4455,6 @@
     }
 
     @Override
-    protected void onRestoreInstanceState(Parcelable state) {
-        super.onRestoreInstanceState(state);
-        Launcher.setScreen(mCurrentPage);
-    }
-
-    @Override
     protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
         // We don't dispatch restoreInstanceState to our children using this code path.
         // Some pages will be restored immediately as their items are bound immediately, and