Merge "Accounting for recent changes in padding when detecting backgound taps to close AllApps." into ub-launcher3-burnaby
diff --git a/proguard.flags b/proguard.flags
index ee6bfa5..e2a4b5b 100644
--- a/proguard.flags
+++ b/proguard.flags
@@ -6,7 +6,6 @@
   public void onClickVoiceButton(android.view.View);
   public void onClickConfigureButton(android.view.View);
   public void onClickAllAppsButton(android.view.View);
-  public void onClickAppMarketButton(android.view.View);
   public void dismissFirstRunCling(android.view.View);
   public void dismissMigrationClingCopyApps(android.view.View);
   public void dismissMigrationClingUseDefault(android.view.View);
diff --git a/res/drawable-hdpi/ic_launcher_market_holo.png b/res/drawable-hdpi/ic_launcher_market_holo.png
deleted file mode 100644
index dc78251..0000000
--- a/res/drawable-hdpi/ic_launcher_market_holo.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_launcher_market_holo.png b/res/drawable-mdpi/ic_launcher_market_holo.png
deleted file mode 100644
index cacb374..0000000
--- a/res/drawable-mdpi/ic_launcher_market_holo.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_launcher_market_holo.png b/res/drawable-xhdpi/ic_launcher_market_holo.png
deleted file mode 100644
index 958f0de..0000000
--- a/res/drawable-xhdpi/ic_launcher_market_holo.png
+++ /dev/null
Binary files differ
diff --git a/res/layout/search_drop_target_bar.xml b/res/layout/search_drop_target_bar.xml
index 0d7167e..9b0da1d 100644
--- a/res/layout/search_drop_target_bar.xml
+++ b/res/layout/search_drop_target_bar.xml
@@ -45,6 +45,19 @@
             style="@style/DropTargetButtonContainer"
             android:layout_weight="1" >
 
+            <!-- Uninstall target -->
+
+            <com.android.launcher3.UninstallDropTarget
+                android:id="@+id/uninstall_target_text"
+                style="@style/DropTargetButton"
+                android:drawableStart="@drawable/uninstall_target_selector"
+                android:text="@string/delete_target_uninstall_label" />
+        </FrameLayout>
+
+        <FrameLayout
+            style="@style/DropTargetButtonContainer"
+            android:layout_weight="1" >
+
             <!-- Info target -->
 
             <com.android.launcher3.InfoDropTarget
diff --git a/src/com/android/launcher3/AppsContainerView.java b/src/com/android/launcher3/AppsContainerView.java
index 376f888..b247145 100644
--- a/src/com/android/launcher3/AppsContainerView.java
+++ b/src/com/android/launcher3/AppsContainerView.java
@@ -34,6 +34,7 @@
 import android.widget.FrameLayout;
 import android.widget.LinearLayout;
 import android.widget.TextView;
+
 import com.android.launcher3.util.Thunk;
 
 import java.util.List;
@@ -297,7 +298,7 @@
 
     @Override
     public boolean supportsDeleteDropTarget() {
-        return true;
+        return false;
     }
 
     @Override
diff --git a/src/com/android/launcher3/ButtonDropTarget.java b/src/com/android/launcher3/ButtonDropTarget.java
index 019f86c..5b39908 100644
--- a/src/com/android/launcher3/ButtonDropTarget.java
+++ b/src/com/android/launcher3/ButtonDropTarget.java
@@ -17,19 +17,29 @@
 package com.android.launcher3;
 
 import android.content.Context;
+import android.content.res.ColorStateList;
+import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.graphics.PointF;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
+import android.graphics.drawable.TransitionDrawable;
 import android.util.AttributeSet;
 import android.view.View;
+import android.view.ViewGroup;
+import android.view.animation.DecelerateInterpolator;
+import android.view.animation.LinearInterpolator;
 import android.widget.TextView;
 
+import com.android.launcher3.R;
+import com.android.launcher3.util.Thunk;
 
 /**
  * Implements a DropTarget.
  */
-public class ButtonDropTarget extends TextView implements DropTarget, DragController.DragListener {
+public abstract class ButtonDropTarget extends TextView implements DropTarget, DragController.DragListener {
+
+    private static int DRAG_VIEW_DROP_DURATION = 285;
 
     protected final int mTransitionDuration;
 
@@ -44,6 +54,9 @@
     /** The paint applied to the drag view on hover */
     protected int mHoverColor = 0;
 
+    protected ColorStateList mOriginalTextColor;
+    protected TransitionDrawable mDrawable;
+
     public ButtonDropTarget(Context context, AttributeSet attrs) {
         this(context, attrs, 0);
     }
@@ -56,12 +69,37 @@
         mBottomDragPadding = r.getDimensionPixelSize(R.dimen.drop_target_drag_padding);
     }
 
-    void setLauncher(Launcher launcher) {
-        mLauncher = launcher;
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mOriginalTextColor = getTextColors();
+
+        // Remove the text in the Phone UI in landscape
+        int orientation = getResources().getConfiguration().orientation;
+        if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
+            if (!LauncherAppState.getInstance().isScreenLarge()) {
+                setText("");
+            }
+        }
     }
 
-    public boolean acceptDrop(DragObject d) {
-        return false;
+    protected void setDrawable(int resId) {
+        // Get the hover color
+        mDrawable = (TransitionDrawable) getCurrentDrawable();
+
+        if (mDrawable == null) {
+            // TODO: investigate why this is ever happening. Presently only on one known device.
+            mDrawable = (TransitionDrawable) getResources().getDrawable(resId);
+            setCompoundDrawablesRelativeWithIntrinsicBounds(mDrawable, null, null, null);
+        }
+
+        if (null != mDrawable) {
+            mDrawable.setCrossFadeEnabled(true);
+        }
+    }
+
+    public void setLauncher(Launcher launcher) {
+        mLauncher = launcher;
     }
 
     public void setSearchDropTargetBar(SearchDropTargetBar searchDropTargetBar) {
@@ -78,37 +116,94 @@
         return null;
     }
 
-    public void onDrop(DragObject d) {
-    }
+    @Override
+    public void onFlingToDelete(DragObject d, int x, int y, PointF vec) { }
 
-    public void onFlingToDelete(DragObject d, int x, int y, PointF vec) {
-        // Do nothing
-    }
-
-    public void onDragEnter(DragObject d) {
+    @Override
+    public final void onDragEnter(DragObject d) {
         d.dragView.setColor(mHoverColor);
+        mDrawable.startTransition(mTransitionDuration);
+        setTextColor(mHoverColor);
     }
 
+    @Override
     public void onDragOver(DragObject d) {
         // Do nothing
     }
 
-    public void onDragExit(DragObject d) {
-        d.dragView.setColor(0);
+    protected void resetHoverColor() {
+        mDrawable.resetTransition();
+        setTextColor(mOriginalTextColor);
     }
 
-    public void onDragStart(DragSource source, Object info, int dragAction) {
-        // Do nothing
+    @Override
+    public final void onDragExit(DragObject d) {
+        if (!d.dragComplete) {
+            d.dragView.setColor(0);
+            resetHoverColor();
+        } else {
+            // Restore the hover color
+            d.dragView.setColor(mHoverColor);
+        }
     }
 
+    @Override
+    public final void onDragStart(DragSource source, Object info, int dragAction) {
+        mActive = supportsDrop(source, info);
+        mDrawable.resetTransition();
+        setTextColor(mOriginalTextColor);
+        ((ViewGroup) getParent()).setVisibility(mActive ? View.VISIBLE : View.GONE);
+    }
+
+    @Override
+    public final boolean acceptDrop(DragObject dragObject) {
+        return supportsDrop(dragObject.dragSource, dragObject.dragInfo);
+    }
+
+    protected abstract boolean supportsDrop(DragSource source, Object info);
+
+    @Override
     public boolean isDropEnabled() {
         return mActive;
     }
 
+    @Override
     public void onDragEnd() {
-        // Do nothing
+        mActive = false;
     }
 
+    /**
+     * On drop animate the dropView to the icon.
+     */
+    @Override
+    public void onDrop(final DragObject d) {
+        final DragLayer dragLayer = mLauncher.getDragLayer();
+        final Rect from = new Rect();
+        dragLayer.getViewRectRelativeToSelf(d.dragView, from);
+
+        int width = mDrawable.getIntrinsicWidth();
+        int height = mDrawable.getIntrinsicHeight();
+        final Rect to = getIconRect(d.dragView.getMeasuredWidth(), d.dragView.getMeasuredHeight(),
+                width, height);
+        final float scale = (float) to.width() / from.width();
+        mSearchDropTargetBar.deferOnDragEnd();
+
+        Runnable onAnimationEndRunnable = new Runnable() {
+            @Override
+            public void run() {
+                completeDrop(d);
+                mSearchDropTargetBar.onDragEnd();
+                mLauncher.exitSpringLoadedDragModeDelayed(true, 0, null);
+            }
+        };
+        dragLayer.animateView(d.dragView, from, to, scale, 1f, 1f, 0.1f, 0.1f,
+                DRAG_VIEW_DROP_DURATION, new DecelerateInterpolator(2),
+                new LinearInterpolator(), onAnimationEndRunnable,
+                DragLayer.ANIMATION_END_DISAPPEAR, null);
+    }
+
+    @Thunk abstract void completeDrop(DragObject d);
+
     @Override
     public void getHitRectRelativeToDragLayer(android.graphics.Rect outRect) {
         super.getHitRect(outRect);
@@ -120,10 +215,10 @@
     }
 
     private boolean isRtl() {
-        return (getLayoutDirection() == View.LAYOUT_DIRECTION_RTL);
+        return (getLayoutDirection() == LAYOUT_DIRECTION_RTL);
     }
 
-    Rect getIconRect(int viewWidth, int viewHeight, int drawableWidth, int drawableHeight) {
+    protected Rect getIconRect(int viewWidth, int viewHeight, int drawableWidth, int drawableHeight) {
         DragLayer dragLayer = mLauncher.getDragLayer();
 
         // Find the rect to animate to (the view is center aligned)
@@ -157,6 +252,7 @@
         return to;
     }
 
+    @Override
     public void getLocationInDragLayer(int[] loc) {
         mLauncher.getDragLayer().getLocationInDragLayer(this, loc);
     }
diff --git a/src/com/android/launcher3/DeleteDropTarget.java b/src/com/android/launcher3/DeleteDropTarget.java
index 62aa285..aa3e66c 100644
--- a/src/com/android/launcher3/DeleteDropTarget.java
+++ b/src/com/android/launcher3/DeleteDropTarget.java
@@ -19,33 +19,22 @@
 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;
-import android.content.res.Configuration;
-import android.content.res.Resources;
 import android.graphics.PointF;
 import android.graphics.Rect;
-import android.graphics.drawable.TransitionDrawable;
 import android.os.AsyncTask;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.UserManager;
 import android.util.AttributeSet;
 import android.view.View;
 import android.view.ViewConfiguration;
-import android.view.ViewGroup;
 import android.view.animation.AnimationUtils;
 import android.view.animation.DecelerateInterpolator;
-import android.view.animation.LinearInterpolator;
 
-import com.android.launcher3.compat.UserHandleCompat;
+import com.android.launcher3.R;
 import com.android.launcher3.util.Thunk;
 import com.android.launcher3.widget.WidgetsContainerView;
 
 public class DeleteDropTarget extends ButtonDropTarget {
-    private static int DELETE_ANIMATION_DURATION = 285;
+
     private static int FLING_DELETE_ANIMATION_DURATION = 350;
     private static float FLING_TO_DELETE_FRICTION = 0.035f;
     private static int MODE_FLING_DELETE_TO_TRASH = 0;
@@ -53,13 +42,6 @@
 
     private final int mFlingDeleteMode = MODE_FLING_DELETE_ALONG_VECTOR;
 
-    private ColorStateList mOriginalTextColor;
-    private TransitionDrawable mUninstallDrawable;
-    private TransitionDrawable mRemoveDrawable;
-    private TransitionDrawable mCurrentDrawable;
-
-    @Thunk boolean mWaitingForUninstall = false;
-
     public DeleteDropTarget(Context context, AttributeSet attrs) {
         this(context, attrs, 0);
     }
@@ -71,258 +53,27 @@
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
-
-        // Get the drawable
-        mOriginalTextColor = getTextColors();
-
         // Get the hover color
-        Resources r = getResources();
-        mHoverColor = r.getColor(R.color.delete_target_hover_tint);
-        mUninstallDrawable = (TransitionDrawable) 
-                r.getDrawable(R.drawable.uninstall_target_selector);
-        mRemoveDrawable = (TransitionDrawable) r.getDrawable(R.drawable.remove_target_selector);
+        mHoverColor = getResources().getColor(R.color.delete_target_hover_tint);
 
-        mRemoveDrawable.setCrossFadeEnabled(true);
-        mUninstallDrawable.setCrossFadeEnabled(true);
-
-        // The current drawable is set to either the remove drawable or the uninstall drawable 
-        // and is initially set to the remove drawable, as set in the layout xml.
-        mCurrentDrawable = (TransitionDrawable) getCurrentDrawable();
-
-        // Remove the text in the Phone UI in landscape
-        int orientation = getResources().getConfiguration().orientation;
-        if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
-            if (!LauncherAppState.getInstance().isScreenLarge()) {
-                setText("");
-            }
-        }
+        setDrawable(R.drawable.remove_target_selector);
     }
 
-    private boolean isAllAppsApplication(DragSource source, Object info) {
-        return source.supportsAppInfoDropTarget() && (info instanceof AppInfo);
-    }
-
-    private boolean isWidget(DragSource source, Object info) {
-        if (source instanceof WidgetsContainerView) {
-            if (info instanceof PendingAddItemInfo) {
-                PendingAddItemInfo addInfo = (PendingAddItemInfo) info;
-                switch (addInfo.itemType) {
-                    case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
-                    case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
-                    case LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET:
-                        return true;
-                }
-            }
-        }
-        return false;
-    }
-    private boolean isDragSourceWorkspaceOrFolder(DragObject d) {
-        return (d.dragSource instanceof Workspace) || (d.dragSource instanceof Folder);
-    }
-
-    private void setHoverColor() {
-        if (mCurrentDrawable != null) {
-            mCurrentDrawable.startTransition(mTransitionDuration);
-        }
-        setTextColor(mHoverColor);
-    }
-    private void resetHoverColor() {
-        if (mCurrentDrawable != null) {
-            mCurrentDrawable.resetTransition();
-        }
-        setTextColor(mOriginalTextColor);
+    public static boolean willAcceptDrop(DragSource source, Object info) {
+        return (info instanceof ItemInfo) && source.supportsDeleteDropTarget();
     }
 
     @Override
-    public boolean acceptDrop(DragObject d) {
-        return willAcceptDrop(d.dragInfo);
-    }
-
-    public static boolean willAcceptDrop(Object info) {
-        if (info instanceof ItemInfo) {
-            ItemInfo item = (ItemInfo) info;
-            if (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET ||
-                    item.itemType == LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET ||
-                    item.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) {
-                return true;
-            }
-
-            if (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER) {
-                return true;
-            }
-
-            if (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION &&
-                    item instanceof AppInfo) {
-                AppInfo appInfo = (AppInfo) info;
-                return (appInfo.flags & AppInfo.DOWNLOADED_FLAG) != 0;
-            }
-
-            if (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION &&
-                item instanceof ShortcutInfo) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
-    @Override
-    public void onDragStart(DragSource source, Object info, int dragAction) {
-        boolean isVisible = true;
-        boolean useUninstallLabel = isAllAppsApplication(source, info);
-        boolean useDeleteLabel = !useUninstallLabel && source.supportsDeleteDropTarget();
-
-        // If we are dragging an application from AppsCustomize, only show the control if we can
-        // delete the app (it was downloaded), and rename the string to "uninstall" in such a case.
-        // Hide the delete target if it is a widget from AppsCustomize.
-        if (!willAcceptDrop(info) || isWidget(source, info)) {
-            isVisible = false;
-        }
-        if (useUninstallLabel) {
-            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
-                UserManager userManager = (UserManager)
-                        getContext().getSystemService(Context.USER_SERVICE);
-                Bundle restrictions = userManager.getUserRestrictions();
-                if (restrictions.getBoolean(UserManager.DISALLOW_APPS_CONTROL, false)
-                        || restrictions.getBoolean(UserManager.DISALLOW_UNINSTALL_APPS, false)) {
-                    isVisible = false;
-                }
-            }
-        }
-
-        if (useUninstallLabel) {
-            setCompoundDrawablesRelativeWithIntrinsicBounds(mUninstallDrawable, null, null, null);
-        } else if (useDeleteLabel) {
-            setCompoundDrawablesRelativeWithIntrinsicBounds(mRemoveDrawable, null, null, null);
-        } else {
-            isVisible = false;
-        }
-        mCurrentDrawable = (TransitionDrawable) getCurrentDrawable();
-
-        mActive = isVisible;
-        resetHoverColor();
-        ((ViewGroup) getParent()).setVisibility(isVisible ? View.VISIBLE : View.GONE);
-        if (isVisible && getText().length() > 0) {
-            setText(useUninstallLabel ? R.string.delete_target_uninstall_label
-                : R.string.delete_target_label);
-        }
+    protected boolean supportsDrop(DragSource source, Object info) {
+        return willAcceptDrop(source, info);
     }
 
     @Override
-    public void onDragEnd() {
-        super.onDragEnd();
-        mActive = false;
-    }
-
-    public void onDragEnter(DragObject d) {
-        super.onDragEnter(d);
-
-        setHoverColor();
-    }
-
-    public void onDragExit(DragObject d) {
-        super.onDragExit(d);
-
-        if (!d.dragComplete) {
-            resetHoverColor();
-        } else {
-            // Restore the hover color if we are deleting
-            d.dragView.setColor(mHoverColor);
-        }
-    }
-
-    private void animateToTrashAndCompleteDrop(final DragObject d) {
-        final DragLayer dragLayer = mLauncher.getDragLayer();
-        final Rect from = new Rect();
-        dragLayer.getViewRectRelativeToSelf(d.dragView, from);
-
-        int width = mCurrentDrawable == null ? 0 : mCurrentDrawable.getIntrinsicWidth();
-        int height = mCurrentDrawable == null ? 0 : mCurrentDrawable.getIntrinsicHeight();
-        final Rect to = getIconRect(d.dragView.getMeasuredWidth(), d.dragView.getMeasuredHeight(),
-                width, height);
-        final float scale = (float) to.width() / from.width();
-
-        mSearchDropTargetBar.deferOnDragEnd();
-        deferCompleteDropIfUninstalling(d);
-
-        Runnable onAnimationEndRunnable = new Runnable() {
-            @Override
-            public void run() {
-                completeDrop(d);
-                mSearchDropTargetBar.onDragEnd();
-                mLauncher.exitSpringLoadedDragModeDelayed(true, 0, null);
-            }
-        };
-        dragLayer.animateView(d.dragView, from, to, scale, 1f, 1f, 0.1f, 0.1f,
-                DELETE_ANIMATION_DURATION, new DecelerateInterpolator(2),
-                new LinearInterpolator(), onAnimationEndRunnable,
-                DragLayer.ANIMATION_END_DISAPPEAR, null);
-    }
-
-    private void deferCompleteDropIfUninstalling(DragObject d) {
-        mWaitingForUninstall = false;
-        if (isUninstallFromWorkspace(d)) {
-            if (d.dragSource instanceof Folder) {
-                ((Folder) d.dragSource).deferCompleteDropAfterUninstallActivity();
-            } else if (d.dragSource instanceof Workspace) {
-                ((Workspace) d.dragSource).deferCompleteDropAfterUninstallActivity();
-            }
-            mWaitingForUninstall = true;
-        }
-    }
-
-    private boolean isUninstallFromWorkspace(DragObject d) {
-        return false;
-    }
-
     @Thunk void completeDrop(DragObject d) {
         ItemInfo item = (ItemInfo) d.dragInfo;
-        boolean wasWaitingForUninstall = mWaitingForUninstall;
-        mWaitingForUninstall = false;
-        if (isAllAppsApplication(d.dragSource, item)) {
-            uninstallApp(mLauncher, (AppInfo) item);
-        } else if (isUninstallFromWorkspace(d)) {
-            ShortcutInfo shortcut = (ShortcutInfo) item;
-            if (shortcut.intent != null && shortcut.intent.getComponent() != null) {
-                final ComponentName componentName = shortcut.intent.getComponent();
-                final DragSource dragSource = d.dragSource;
-                final UserHandleCompat user = shortcut.user;
-                mWaitingForUninstall = mLauncher.startApplicationUninstallActivity(
-                        componentName, shortcut.flags, user);
-                if (mWaitingForUninstall) {
-                    final Runnable checkIfUninstallWasSuccess = new Runnable() {
-                        @Override
-                        public void run() {
-                            mWaitingForUninstall = false;
-                            String packageName = componentName.getPackageName();
-                            boolean uninstallSuccessful = !AllAppsList.packageHasActivities(
-                                    getContext(), packageName, user);
-                            if (dragSource instanceof Folder) {
-                                ((Folder) dragSource).
-                                    onUninstallActivityReturned(uninstallSuccessful);
-                            } else if (dragSource instanceof Workspace) {
-                                ((Workspace) dragSource).
-                                    onUninstallActivityReturned(uninstallSuccessful);
-                            }
-                        }
-                    };
-                    mLauncher.addOnResumeCallback(checkIfUninstallWasSuccess);
-                }
-            }
-        } else if (isDragSourceWorkspaceOrFolder(d)) {
+        if ((d.dragSource instanceof Workspace) || (d.dragSource instanceof Folder)) {
             removeWorkspaceOrFolderItem(mLauncher, item, null);
         }
-        if (wasWaitingForUninstall && !mWaitingForUninstall) {
-            if (d.dragSource instanceof Folder) {
-                ((Folder) d.dragSource).onUninstallActivityReturned(false);
-            } else if (d.dragSource instanceof Workspace) {
-                ((Workspace) d.dragSource).onUninstallActivityReturned(false);
-            }
-        }
-    }
-
-    public static void uninstallApp(Launcher launcher, AppInfo info) {
-        launcher.startApplicationUninstallActivity(info.componentName, info.flags, info.user);
     }
 
     /**
@@ -354,7 +105,7 @@
                         appWidgetHost.deleteAppWidgetId(widget.appWidgetId);
                         return null;
                     }
-                }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void) null);
+                }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
             }
         } else {
             return false;
@@ -367,18 +118,14 @@
         return true;
     }
 
-    public void onDrop(DragObject d) {
-        animateToTrashAndCompleteDrop(d);
-    }
-
     /**
      * Creates an animation from the current drag view to the delete trash icon.
      */
     private AnimatorUpdateListener createFlingToTrashAnimatorListener(final DragLayer dragLayer,
             DragObject d, PointF vel, ViewConfiguration config) {
 
-        int width = mCurrentDrawable == null ? 0 : mCurrentDrawable.getIntrinsicWidth();
-        int height = mCurrentDrawable == null ? 0 : mCurrentDrawable.getIntrinsicHeight();
+        int width = mDrawable.getIntrinsicWidth();
+        int height = mDrawable.getIntrinsicHeight();
         final Rect to = getIconRect(d.dragView.getMeasuredWidth(), d.dragView.getMeasuredHeight(),
                 width, height);
         final Rect from = new Rect();
@@ -541,7 +288,6 @@
             updateCb = createFlingAlongVectorAnimatorListener(dragLayer, d, vel, startTime,
                     duration, config);
         }
-        deferCompleteDropIfUninstalling(d);
 
         Runnable onAnimationEndRunnable = new Runnable() {
             @Override
diff --git a/src/com/android/launcher3/DragController.java b/src/com/android/launcher3/DragController.java
index 3473747..b24608c 100644
--- a/src/com/android/launcher3/DragController.java
+++ b/src/com/android/launcher3/DragController.java
@@ -125,7 +125,7 @@
     /**
      * Interface to receive notifications when a drag starts or stops
      */
-    interface DragListener {
+    public interface DragListener {
         /**
          * A drag has begun
          *
@@ -400,7 +400,7 @@
         }
     }
 
-    void onDeferredEndFling(DropTarget.DragObject d) {
+    public void onDeferredEndFling(DropTarget.DragObject d) {
         d.dragSource.onFlingToDeleteCompleted();
     }
 
@@ -462,7 +462,8 @@
                 mLastTouchUpTime = System.currentTimeMillis();
                 if (mDragging) {
                     PointF vec = isFlingingToDelete(mDragObject.dragSource);
-                    if (!DeleteDropTarget.willAcceptDrop(mDragObject.dragInfo)) {
+                    if (!DeleteDropTarget.willAcceptDrop(mDragObject.dragSource,
+                            mDragObject.dragInfo)) {
                         vec = null;
                     }
                     if (vec != null) {
@@ -616,7 +617,7 @@
 
             if (mDragging) {
                 PointF vec = isFlingingToDelete(mDragObject.dragSource);
-                if (!DeleteDropTarget.willAcceptDrop(mDragObject.dragInfo)) {
+                if (!DeleteDropTarget.willAcceptDrop(mDragObject.dragSource, mDragObject.dragInfo)) {
                     vec = null;
                 }
                 if (vec != null) {
diff --git a/src/com/android/launcher3/DropTarget.java b/src/com/android/launcher3/DropTarget.java
index 94ae82b..c5cca3b 100644
--- a/src/com/android/launcher3/DropTarget.java
+++ b/src/com/android/launcher3/DropTarget.java
@@ -139,7 +139,7 @@
 
     /**
      * Handle an object being dropped on the DropTarget
-     * 
+     *
      * @param source DragSource where the drag started
      * @param x X coordinate of the drop location
      * @param y Y coordinate of the drop location
@@ -169,7 +169,7 @@
     /**
      * Check if a drop action can occur at, or near, the requested location.
      * This will be called just before onDrop.
-     * 
+     *
      * @param source DragSource where the drag started
      * @param x X coordinate of the drop location
      * @param y Y coordinate of the drop location
diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java
index 1163324..c35ce94 100644
--- a/src/com/android/launcher3/Folder.java
+++ b/src/com/android/launcher3/Folder.java
@@ -51,6 +51,7 @@
 
 import com.android.launcher3.DragController.DragListener;
 import com.android.launcher3.FolderInfo.FolderListener;
+import com.android.launcher3.UninstallDropTarget.UninstallSource;
 import com.android.launcher3.Workspace.ItemOperator;
 import com.android.launcher3.util.Thunk;
 
@@ -62,7 +63,7 @@
  */
 public class Folder extends LinearLayout implements DragSource, View.OnClickListener,
         View.OnLongClickListener, DropTarget, FolderListener, TextView.OnEditorActionListener,
-        View.OnFocusChangeListener, DragListener {
+        View.OnFocusChangeListener, DragListener, UninstallSource {
     private static final String TAG = "Launcher.Folder";
 
     /**
@@ -772,10 +773,12 @@
         updateItemLocationsInDatabaseBatch();
     }
 
+    @Override
     public void deferCompleteDropAfterUninstallActivity() {
         mDeferDropAfterUninstall = true;
     }
 
+    @Override
     public void onUninstallActivityReturned(boolean success) {
         mDeferDropAfterUninstall = false;
         mUninstallSuccessful = success;
diff --git a/src/com/android/launcher3/InfoDropTarget.java b/src/com/android/launcher3/InfoDropTarget.java
index 3c36361..e48640c 100644
--- a/src/com/android/launcher3/InfoDropTarget.java
+++ b/src/com/android/launcher3/InfoDropTarget.java
@@ -18,21 +18,14 @@
 
 import android.content.ComponentName;
 import android.content.Context;
-import android.content.res.ColorStateList;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.graphics.drawable.TransitionDrawable;
+import android.provider.Settings;
 import android.util.AttributeSet;
-import android.view.View;
-import android.view.ViewGroup;
 
+import com.android.launcher3.R;
 import com.android.launcher3.compat.UserHandleCompat;
 
 public class InfoDropTarget extends ButtonDropTarget {
 
-    private ColorStateList mOriginalTextColor;
-    private TransitionDrawable mDrawable;
-
     public InfoDropTarget(Context context, AttributeSet attrs) {
         this(context, attrs, 0);
     }
@@ -44,43 +37,10 @@
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
-
-        mOriginalTextColor = getTextColors();
-
         // Get the hover color
-        Resources r = getResources();
-        mHoverColor = r.getColor(R.color.info_target_hover_tint);
-        mDrawable = (TransitionDrawable) getCurrentDrawable();
+        mHoverColor = getResources().getColor(R.color.info_target_hover_tint);
 
-        if (mDrawable == null) {
-            // TODO: investigate why this is ever happening. Presently only on one known device.
-            mDrawable = (TransitionDrawable) r.getDrawable(R.drawable.info_target_selector);
-            setCompoundDrawablesRelativeWithIntrinsicBounds(mDrawable, null, null, null);
-        }
-
-        if (null != mDrawable) {
-            mDrawable.setCrossFadeEnabled(true);
-        }
-
-        // Remove the text in the Phone UI in landscape
-        int orientation = getResources().getConfiguration().orientation;
-        if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
-            if (!LauncherAppState.getInstance().isScreenLarge()) {
-                setText("");
-            }
-        }
-    }
-
-    @Override
-    public boolean acceptDrop(DragObject d) {
-        // 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;
+        setDrawable(R.drawable.info_target_selector);
     }
 
     public static void startDetailsActivityForInfo(Object info, Launcher launcher) {
@@ -105,39 +65,14 @@
     }
 
     @Override
-    public void onDragStart(DragSource source, Object info, int dragAction) {
-        boolean isVisible = true;
-
-        // Hide this button unless we are dragging something from AllApps
-        if (!source.supportsAppInfoDropTarget()) {
-            isVisible = false;
-        }
-
-        mActive = isVisible;
-        mDrawable.resetTransition();
-        setTextColor(mOriginalTextColor);
-        ((ViewGroup) getParent()).setVisibility(isVisible ? View.VISIBLE : View.GONE);
+    protected boolean supportsDrop(DragSource source, Object info) {
+        return source.supportsAppInfoDropTarget() &&
+                Settings.Global.getInt(getContext().getContentResolver(),
+                        Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) == 1;
     }
 
     @Override
-    public void onDragEnd() {
-        super.onDragEnd();
-        mActive = false;
-    }
-
-    public void onDragEnter(DragObject d) {
-        super.onDragEnter(d);
-
-        mDrawable.startTransition(mTransitionDuration);
-        setTextColor(mHoverColor);
-    }
-
-    public void onDragExit(DragObject d) {
-        super.onDragExit(d);
-
-        if (!d.dragComplete) {
-            mDrawable.resetTransition();
-            setTextColor(mOriginalTextColor);
-        }
+    void completeDrop(DragObject d) {
+        startDetailsActivityForInfo(d.dragInfo, mLauncher);
     }
 }
diff --git a/src/com/android/launcher3/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/LauncherAccessibilityDelegate.java
index cfc1bd9..8ba02ea 100644
--- a/src/com/android/launcher3/LauncherAccessibilityDelegate.java
+++ b/src/com/android/launcher3/LauncherAccessibilityDelegate.java
@@ -105,7 +105,8 @@
             InfoDropTarget.startDetailsActivityForInfo(item, mLauncher);
             return true;
         } else if (action == UNINSTALL) {
-            DeleteDropTarget.uninstallApp(mLauncher, (AppInfo) item);
+            AppInfo info = (AppInfo) item;
+            mLauncher.startApplicationUninstallActivity(info.componentName, info.flags, info.user);
             return true;
         } else if (action == MOVE) {
             beginAccessibleDrag(host, item);
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index 37f1ea8..f7df6bc 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -1079,7 +1079,7 @@
      * @param context
      * @param item
      */
-    static void deleteItemFromDatabase(Context context, final ItemInfo item) {
+    public static void deleteItemFromDatabase(Context context, final ItemInfo item) {
         ArrayList<ItemInfo> items = new ArrayList<ItemInfo>();
         items.add(item);
         deleteItemsFromDatabase(context, items);
@@ -1185,7 +1185,7 @@
     /**
      * Remove the contents of the specified folder from the database
      */
-    static void deleteFolderContentsFromDatabase(Context context, final FolderInfo info) {
+    public static void deleteFolderContentsFromDatabase(Context context, final FolderInfo info) {
         final ContentResolver cr = context.getContentResolver();
 
         Runnable r = new Runnable() {
@@ -3106,6 +3106,9 @@
                                     si.status &= ~ShortcutInfo.FLAG_RESTORED_ICON
                                             & ~ShortcutInfo.FLAG_AUTOINTALL_ICON
                                             & ~ShortcutInfo.FLAG_INSTALL_SESSION_ACTIVE;
+                                    if (appInfo != null) {
+                                        si.flags = appInfo.flags;
+                                    }
 
                                     infoUpdated = true;
                                     si.updateIcon(mIconCache);
@@ -3414,6 +3417,9 @@
         info.user = user;
         info.contentDescription = mUserManager.getBadgedLabelForUser(
                 info.title.toString(), info.user);
+        if (lai != null) {
+            info.flags = AppInfo.initFlags(lai);
+        }
         return info;
     }
 
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index d75ef73..f9f5ae1 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -69,8 +69,8 @@
     static final String OLD_AUTHORITY = "com.android.launcher2.settings";
     static final String AUTHORITY = ProviderConfig.AUTHORITY;
 
-    static final String TABLE_FAVORITES = "favorites";
-    static final String TABLE_WORKSPACE_SCREENS = "workspaceScreens";
+    static final String TABLE_FAVORITES = LauncherSettings.Favorites.TABLE_NAME;
+    static final String TABLE_WORKSPACE_SCREENS = LauncherSettings.WorkspaceScreens.TABLE_NAME;
     static final String EMPTY_DATABASE_CREATED = "EMPTY_DATABASE_CREATED";
 
     private static final String URI_PARAM_IS_EXTERNAL_ADD = "isExternalAdd";
diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java
index 111de40..90e60e4 100644
--- a/src/com/android/launcher3/LauncherSettings.java
+++ b/src/com/android/launcher3/LauncherSettings.java
@@ -19,6 +19,8 @@
 import android.net.Uri;
 import android.provider.BaseColumns;
 
+import com.android.launcher3.config.ProviderConfig;
+
 /**
  * Settings related utilities.
  */
@@ -45,7 +47,7 @@
          * an Intent that can be launched.
          * <P>Type: TEXT</P>
          */
-        static final String INTENT = "intent";
+        public static final String INTENT = "intent";
 
         /**
          * The type of the gesture
@@ -104,29 +106,35 @@
      *
      * Tracks the order of workspace screens.
      */
-    static final class WorkspaceScreens implements ChangeLogColumns {
+    public static final class WorkspaceScreens implements ChangeLogColumns {
+
+        public static final String TABLE_NAME = "workspaceScreens";
+
         /**
          * The content:// style URL for this table
          */
         static final Uri CONTENT_URI = Uri.parse("content://" +
-                LauncherProvider.AUTHORITY + "/" + LauncherProvider.TABLE_WORKSPACE_SCREENS);
+                ProviderConfig.AUTHORITY + "/" + TABLE_NAME);
 
         /**
          * The rank of this screen -- ie. how it is ordered relative to the other screens.
          * <P>Type: INTEGER</P>
          */
-        static final String SCREEN_RANK = "screenRank";
+        public static final String SCREEN_RANK = "screenRank";
     }
 
     /**
      * Favorites.
      */
     public static final class Favorites implements BaseLauncherColumns {
+
+        public static final String TABLE_NAME = "favorites";
+
         /**
          * The content:// style URL for this table
          */
-        static final Uri CONTENT_URI = Uri.parse("content://" +
-                LauncherProvider.AUTHORITY + "/" + LauncherProvider.TABLE_FAVORITES);
+        public static final Uri CONTENT_URI = Uri.parse("content://" +
+                ProviderConfig.AUTHORITY + "/" + TABLE_NAME);
 
         /**
          * The content:// style URL for a given row, identified by its id.
@@ -136,21 +144,21 @@
          * @return The unique content URL for the specified row.
          */
         static Uri getContentUri(long id) {
-            return Uri.parse("content://" + LauncherProvider.AUTHORITY +
-                    "/" + LauncherProvider.TABLE_FAVORITES + "/" + id);
+            return Uri.parse("content://" + ProviderConfig.AUTHORITY +
+                    "/" + TABLE_NAME + "/" + id);
         }
 
         /**
          * The container holding the favorite
          * <P>Type: INTEGER</P>
          */
-        static final String CONTAINER = "container";
+        public static final String CONTAINER = "container";
 
         /**
          * The icon is a resource identified by a package name and an integer id.
          */
-        static final int CONTAINER_DESKTOP = -100;
-        static final int CONTAINER_HOTSEAT = -101;
+        public static final int CONTAINER_DESKTOP = -100;
+        public static final int CONTAINER_HOTSEAT = -101;
 
         static final String containerToString(int container) {
             switch (container) {
@@ -164,7 +172,7 @@
          * The screen holding the favorite (if container is CONTAINER_DESKTOP)
          * <P>Type: INTEGER</P>
          */
-        static final String SCREEN = "screen";
+        public static final String SCREEN = "screen";
 
         /**
          * The X coordinate of the cell holding the favorite
diff --git a/src/com/android/launcher3/SearchDropTargetBar.java b/src/com/android/launcher3/SearchDropTargetBar.java
index cc17820..a8dcd0f 100644
--- a/src/com/android/launcher3/SearchDropTargetBar.java
+++ b/src/com/android/launcher3/SearchDropTargetBar.java
@@ -44,11 +44,14 @@
     private boolean mIsSearchBarHidden;
     private View mQSBSearchBar;
     private View mDropTargetBar;
-    private ButtonDropTarget mInfoDropTarget;
-    private ButtonDropTarget mDeleteDropTarget;
     private int mBarHeight;
     private boolean mDeferOnDragEnd = false;
 
+    // Drop targets
+    private ButtonDropTarget mInfoDropTarget;
+    private ButtonDropTarget mDeleteDropTarget;
+    private ButtonDropTarget mUninstallDropTarget;
+
     private boolean mEnableDropDownDropTargets;
 
     public SearchDropTargetBar(Context context, AttributeSet attrs) {
@@ -61,13 +64,19 @@
 
     public void setup(Launcher launcher, DragController dragController) {
         dragController.addDragListener(this);
+        dragController.setFlingToDeleteDropTarget(mDeleteDropTarget);
+
         dragController.addDragListener(mInfoDropTarget);
         dragController.addDragListener(mDeleteDropTarget);
+        dragController.addDragListener(mUninstallDropTarget);
+
         dragController.addDropTarget(mInfoDropTarget);
         dragController.addDropTarget(mDeleteDropTarget);
-        dragController.setFlingToDeleteDropTarget(mDeleteDropTarget);
+        dragController.addDropTarget(mUninstallDropTarget);
+
         mInfoDropTarget.setLauncher(launcher);
         mDeleteDropTarget.setLauncher(launcher);
+        mUninstallDropTarget.setLauncher(launcher);
     }
 
     public void setQsbSearchBar(View qsb) {
@@ -116,9 +125,11 @@
         mDropTargetBar = findViewById(R.id.drag_target_bar);
         mInfoDropTarget = (ButtonDropTarget) mDropTargetBar.findViewById(R.id.info_target_text);
         mDeleteDropTarget = (ButtonDropTarget) mDropTargetBar.findViewById(R.id.delete_target_text);
+        mUninstallDropTarget = (ButtonDropTarget) mDropTargetBar.findViewById(R.id.uninstall_target_text);
 
         mInfoDropTarget.setSearchDropTargetBar(this);
         mDeleteDropTarget.setSearchDropTargetBar(this);
+        mUninstallDropTarget.setSearchDropTargetBar(this);
 
         mEnableDropDownDropTargets =
             getResources().getBoolean(R.bool.config_useDropTargetDownTransition);
diff --git a/src/com/android/launcher3/UninstallDropTarget.java b/src/com/android/launcher3/UninstallDropTarget.java
new file mode 100644
index 0000000..4a7fffe
--- /dev/null
+++ b/src/com/android/launcher3/UninstallDropTarget.java
@@ -0,0 +1,122 @@
+package com.android.launcher3;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.UserManager;
+import android.util.AttributeSet;
+import android.util.Pair;
+
+import com.android.launcher3.R;
+import com.android.launcher3.compat.UserHandleCompat;
+import com.android.launcher3.util.Thunk;
+
+public class UninstallDropTarget extends ButtonDropTarget {
+
+    public UninstallDropTarget(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public UninstallDropTarget(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        // Get the hover color
+        mHoverColor = getResources().getColor(R.color.delete_target_hover_tint);
+
+        setDrawable(R.drawable.uninstall_target_selector);
+    }
+
+    @Override
+    protected boolean supportsDrop(DragSource source, Object info) {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
+            UserManager userManager = (UserManager)
+                    getContext().getSystemService(Context.USER_SERVICE);
+            Bundle restrictions = userManager.getUserRestrictions();
+            if (restrictions.getBoolean(UserManager.DISALLOW_APPS_CONTROL, false)
+                    || restrictions.getBoolean(UserManager.DISALLOW_UNINSTALL_APPS, false)) {
+                return false;
+            }
+        }
+
+        Pair<ComponentName, Integer> componentInfo = getAppInfoFlags(info);
+        return componentInfo != null && (componentInfo.second & AppInfo.DOWNLOADED_FLAG) != 0;
+    }
+
+    /**
+     * @return the component name and flags if {@param info} is an AppInfo or an app shortcut.
+     */
+    private static Pair<ComponentName, Integer> getAppInfoFlags(Object item) {
+        if (item instanceof AppInfo) {
+            AppInfo info = (AppInfo) item;
+            return Pair.create(info.componentName, info.flags);
+        } else if (item instanceof ShortcutInfo) {
+            ShortcutInfo info = (ShortcutInfo) item;
+            ComponentName component = info.getTargetComponent();
+            if (info.itemType == LauncherSettings.BaseLauncherColumns.ITEM_TYPE_APPLICATION
+                    && component != null) {
+                return Pair.create(component, info.flags);
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public void onDrop(DragObject d) {
+        // Differ item deletion
+        if (d.dragSource instanceof UninstallSource) {
+            ((UninstallSource) d.dragSource).deferCompleteDropAfterUninstallActivity();
+        }
+        super.onDrop(d);
+    }
+
+    @Override
+    void completeDrop(final DragObject d) {
+        final Pair<ComponentName, Integer> componentInfo = getAppInfoFlags(d.dragInfo);
+        final UserHandleCompat user = ((ItemInfo) d.dragInfo).user;
+        if (mLauncher.startApplicationUninstallActivity(
+                componentInfo.first, componentInfo.second, user)) {
+
+            final Runnable checkIfUninstallWasSuccess = new Runnable() {
+                @Override
+                public void run() {
+                    String packageName = componentInfo.first.getPackageName();
+                    boolean uninstallSuccessful = !AllAppsList.packageHasActivities(
+                            getContext(), packageName, user);
+                    sendUninstallResult(d.dragSource, uninstallSuccessful);
+                }
+            };
+            mLauncher.addOnResumeCallback(checkIfUninstallWasSuccess);
+        } else {
+            sendUninstallResult(d.dragSource, false);
+        }
+    }
+
+    @Thunk void sendUninstallResult(DragSource target, boolean result) {
+        if (target instanceof UninstallSource) {
+            ((UninstallSource) target).onUninstallActivityReturned(result);
+        }
+    }
+
+    /**
+     * Interface defining an object that can provide uninstallable drag objects.
+     */
+    public static interface UninstallSource {
+
+        /**
+         * A pending uninstall operation was complete.
+         * @param result true if uninstall was successful, false otherwise.
+         */
+        void onUninstallActivityReturned(boolean result);
+
+        /**
+         * Indicates that an uninstall request are made and the actual result may come
+         * after some time.
+         */
+        void deferCompleteDropAfterUninstallActivity();
+    }
+}
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 671dcaa..6b03e31 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -66,6 +66,7 @@
 import com.android.launcher3.Launcher.CustomContentCallbacks;
 import com.android.launcher3.Launcher.LauncherOverlay;
 import com.android.launcher3.LauncherSettings.Favorites;
+import com.android.launcher3.UninstallDropTarget.UninstallSource;
 import com.android.launcher3.compat.PackageInstallerCompat;
 import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo;
 import com.android.launcher3.compat.UserHandleCompat;
@@ -88,7 +89,7 @@
 public class Workspace extends SmoothPagedView
         implements DropTarget, DragSource, DragScroller, View.OnTouchListener,
         DragController.DragListener, LauncherTransitionable, ViewGroup.OnHierarchyChangeListener,
-        Insettable {
+        Insettable, UninstallSource {
     private static final String TAG = "Launcher.Workspace";
 
     private static final int CHILDREN_OUTLINE_FADE_OUT_DELAY = 0;
@@ -4271,11 +4272,13 @@
         }
     }
 
+    @Override
     public void deferCompleteDropAfterUninstallActivity() {
         mDeferDropAfterUninstall = true;
     }
 
     /// maybe move this into a smaller part
+    @Override
     public void onUninstallActivityReturned(boolean success) {
         mDeferDropAfterUninstall = false;
         mUninstallSuccessful = success;