Add drop animation / Toast to widgettray

- show instruction toast on clicking the widget
- Add animation when widget drops on the workspace.
Added WidgetHostViewLoader to handle short press and assign widget
host view to enable animation

b/19897708

Change-Id: Iec36d72cb21bf09343d0beeb31a09bf8b0cb5e0d
diff --git a/src/com/android/launcher3/AppWidgetResizeFrame.java b/src/com/android/launcher3/AppWidgetResizeFrame.java
index 2402507..3c698c0 100644
--- a/src/com/android/launcher3/AppWidgetResizeFrame.java
+++ b/src/com/android/launcher3/AppWidgetResizeFrame.java
@@ -349,7 +349,7 @@
                 mTmpRect.right, mTmpRect.bottom);
     }
 
-    static Rect getWidgetSizeRanges(Launcher launcher, int spanX, int spanY, Rect rect) {
+    public static Rect getWidgetSizeRanges(Launcher launcher, int spanX, int spanY, Rect rect) {
         if (rect == null) {
             rect = new Rect();
         }
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 7364a9f..c708b3c 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -104,6 +104,7 @@
 import com.android.launcher3.compat.UserManagerCompat;
 import com.android.launcher3.util.Thunk;
 import com.android.launcher3.widget.PendingAddWidgetInfo;
+import com.android.launcher3.widget.WidgetHostViewLoader;
 import com.android.launcher3.widget.WidgetsContainerView;
 
 import java.io.DataInputStream;
@@ -3953,7 +3954,7 @@
             pendingInfo.minSpanX = item.minSpanX;
             pendingInfo.minSpanY = item.minSpanY;
             Bundle options = null;
-            //        AppsCustomizePagedView.getDefaultOptionsForWidget(this, pendingInfo);
+                    WidgetHostViewLoader.getDefaultOptionsForWidget(this, pendingInfo);
 
             int newWidgetId = mAppWidgetHost.allocateAppWidgetId();
             boolean success = mAppWidgetManager.bindAppWidgetIdIfAllowed(
diff --git a/src/com/android/launcher3/widget/WidgetCell.java b/src/com/android/launcher3/widget/WidgetCell.java
index 1ae75c3..0bc7333 100644
--- a/src/com/android/launcher3/widget/WidgetCell.java
+++ b/src/com/android/launcher3/widget/WidgetCell.java
@@ -24,8 +24,6 @@
 import android.graphics.Rect;
 import android.util.AttributeSet;
 import android.util.Log;
-import android.util.TypedValue;
-import android.view.MotionEvent;
 import android.view.View;
 import android.view.View.OnLayoutChangeListener;
 import android.widget.ImageView;
@@ -43,7 +41,7 @@
 import com.android.launcher3.compat.AppWidgetManagerCompat;
 
 /**
- * The linear layout used strictly for the widget tray.
+ * Represents the individual cell of the widget inside the widget tray.
  */
 public class WidgetCell extends LinearLayout implements OnLayoutChangeListener {
 
@@ -53,14 +51,12 @@
     private static final int FADE_IN_DURATION_MS = 70;
     private int mPresetPreviewSize;
 
-    private static WidgetCell sShortpressTarget = null;
-
+    private ImageView mWidgetImage;
+    private TextView mWidgetName;
+    private TextView mWidgetDims;
     private final Rect mOriginalImagePadding = new Rect();
 
     private String mDimensionsFormatString;
-    private CheckForShortPress mPendingCheckForShortPress = null;
-    private ShortPressListener mShortPressListener = null;
-    private boolean mShortPressTriggered = false;
     private boolean mIsAppWidget;
     private Object mInfo;
 
@@ -92,57 +88,27 @@
     protected void onFinishInflate() {
         super.onFinishInflate();
 
-        final ImageView image = (ImageView) findViewById(R.id.widget_preview);
-        mOriginalImagePadding.left = image.getPaddingLeft();
-        mOriginalImagePadding.top = image.getPaddingTop();
-        mOriginalImagePadding.right = image.getPaddingRight();
-        mOriginalImagePadding.bottom = image.getPaddingBottom();
+        mWidgetImage = (ImageView) findViewById(R.id.widget_preview);
+        mOriginalImagePadding.left = mWidgetImage.getPaddingLeft();
+        mOriginalImagePadding.top = mWidgetImage.getPaddingTop();
+        mOriginalImagePadding.right = mWidgetImage.getPaddingRight();
+        mOriginalImagePadding.bottom = mWidgetImage.getPaddingBottom();
 
         // Ensure we are using the right text size
-        LauncherAppState app = LauncherAppState.getInstance();
-        DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
-        TextView name = (TextView) findViewById(R.id.widget_name);
-        if (name != null) {
-            name.setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.iconTextSizePx);
-        }
-        TextView dims = (TextView) findViewById(R.id.widget_dims);
-        if (dims != null) {
-            dims.setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.iconTextSizePx);
-        }
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        if (DEBUG) {
-            Log.d(TAG, String.format("[tag=%s] onDetachedFromWindow", getTagToString()));
-        }
-        super.onDetachedFromWindow();
-        deletePreview(false);
+        DeviceProfile profile = LauncherAppState.getInstance().getDynamicGrid().getDeviceProfile();
+        mWidgetName = ((TextView) findViewById(R.id.widget_name));
+        mWidgetDims = ((TextView) findViewById(R.id.widget_dims));
     }
 
     public void reset() {
-        ImageView image = (ImageView) findViewById(R.id.widget_preview);
-        final TextView name = (TextView) findViewById(R.id.widget_name);
-        final TextView dims = (TextView) findViewById(R.id.widget_dims);
-        image.setImageDrawable(null);
-        name.setText(null);
-        dims.setText(null);
+        mWidgetImage.setImageDrawable(null);
+        mWidgetName.setText(null);
+        mWidgetDims.setText(null);
     }
 
-    public void deletePreview(boolean recycleImage) {
-        if (recycleImage) {
-            final ImageView image = (ImageView) findViewById(R.id.widget_preview);
-            if (image != null) {
-                image.setImageDrawable(null);
-            }
-        }
-
-        if (mActiveRequest != null) {
-            mActiveRequest.cancel(recycleImage);
-            mActiveRequest = null;
-        }
-    }
-
+    /**
+     * Apply the widget provider info to the view.
+     */
     public void applyFromAppWidgetProviderInfo(LauncherAppWidgetProviderInfo info,
             int maxWidth, WidgetPreviewLoader loader) {
         LauncherAppState app = LauncherAppState.getInstance();
@@ -150,37 +116,41 @@
 
         mIsAppWidget = true;
         mInfo = info;
-        final ImageView image = (ImageView) findViewById(R.id.widget_preview);
         if (maxWidth > -1) {
-            image.setMaxWidth(maxWidth);
+            mWidgetImage.setMaxWidth(maxWidth);
         }
-        final TextView name = (TextView) findViewById(R.id.widget_name);
-        name.setText(AppWidgetManagerCompat.getInstance(getContext()).loadLabel(info));
-        final TextView dims = (TextView) findViewById(R.id.widget_dims);
-        if (dims != null) {
-            int hSpan = Math.min(info.spanX, (int) grid.numColumns);
-            int vSpan = Math.min(info.spanY, (int) grid.numRows);
-            dims.setText(String.format(mDimensionsFormatString, hSpan, vSpan));
-        }
+        // TODO(hyunyoungs): setup a cache for these labels.
+        mWidgetName.setText(AppWidgetManagerCompat.getInstance(getContext()).loadLabel(info));
+        int hSpan = Math.min(info.spanX, (int) grid.numColumns);
+        int vSpan = Math.min(info.spanY, (int) grid.numRows);
+        mWidgetDims.setText(String.format(mDimensionsFormatString, hSpan, vSpan));
         mWidgetPreviewLoader = loader;
     }
 
+    /**
+     * Apply the resolve info to the view.
+     */
     public void applyFromResolveInfo(
             PackageManager pm, ResolveInfo info, WidgetPreviewLoader loader) {
         mIsAppWidget = false;
         mInfo = info;
         CharSequence label = info.loadLabel(pm);
-        final TextView name = (TextView) findViewById(R.id.widget_name);
-        name.setText(label);
-        final TextView dims = (TextView) findViewById(R.id.widget_dims);
-        if (dims != null) {
-            dims.setText(String.format(mDimensionsFormatString, 1, 1));
-        }
+        mWidgetName.setText(label);
+        mWidgetDims.setText(String.format(mDimensionsFormatString, 1, 1));
         mWidgetPreviewLoader = loader;
     }
 
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        deletePreview(false);
+
+        if (DEBUG) {
+            Log.d(TAG, String.format("[tag=%s] onDetachedFromWindow", getTagToString()));
+        }
+    }
+
     public int[] getPreviewSize() {
-        final ImageView i = (ImageView) findViewById(R.id.widget_preview);
         int[] maxSize = new int[2];
         maxSize[0] = mPresetPreviewSize;
         maxSize[1] = mPresetPreviewSize;
@@ -189,110 +159,28 @@
 
     public void applyPreview(Bitmap bitmap) {
         FastBitmapDrawable preview = new FastBitmapDrawable(bitmap);
-        final WidgetImageView image =
-            (WidgetImageView) findViewById(R.id.widget_preview);
         if (DEBUG) {
             Log.d(TAG, String.format("[tag=%s] applyPreview preview: %s",
                     getTagToString(), preview));
         }
         if (preview != null) {
-            image.mAllowRequestLayout = false;
-            image.setImageDrawable(preview);
+            mWidgetImage.setImageDrawable(preview);
             if (mIsAppWidget) {
                 // center horizontally
                 int[] imageSize = getPreviewSize();
                 int centerAmount = (imageSize[0] - preview.getIntrinsicWidth()) / 2;
-                image.setPadding(mOriginalImagePadding.left + centerAmount,
+                mWidgetImage.setPadding(mOriginalImagePadding.left + centerAmount,
                         mOriginalImagePadding.top,
                         mOriginalImagePadding.right,
                         mOriginalImagePadding.bottom);
             }
-            image.setAlpha(0f);
-            image.animate().alpha(1.0f).setDuration(FADE_IN_DURATION_MS);
-            image.mAllowRequestLayout = true;
-            image.requestLayout();
+            mWidgetImage.setAlpha(0f);
+            mWidgetImage.animate().alpha(1.0f).setDuration(FADE_IN_DURATION_MS);
+            // TODO(hyunyoungs): figure out why this has to be called explicitly.
+            mWidgetImage.requestLayout();
         }
     }
 
-    void setShortPressListener(ShortPressListener listener) {
-        mShortPressListener = listener;
-    }
-
-    interface ShortPressListener {
-        void onShortPress(View v);
-        void cleanUpShortPress(View v);
-    }
-
-    class CheckForShortPress implements Runnable {
-        public void run() {
-            if (sShortpressTarget != null) return;
-            if (mShortPressListener != null) {
-                mShortPressListener.onShortPress(WidgetCell.this);
-                sShortpressTarget = WidgetCell.this;
-            }
-            mShortPressTriggered = true;
-        }
-    }
-
-    private void checkForShortPress() {
-        if (sShortpressTarget != null) return;
-        if (mPendingCheckForShortPress == null) {
-            mPendingCheckForShortPress = new CheckForShortPress();
-        }
-        postDelayed(mPendingCheckForShortPress, 120);
-    }
-
-    /**
-     * Remove the longpress detection timer.
-     */
-    private void removeShortPressCallback() {
-        if (mPendingCheckForShortPress != null) {
-          removeCallbacks(mPendingCheckForShortPress);
-        }
-    }
-
-    private void cleanUpShortPress() {
-        removeShortPressCallback();
-        if (mShortPressTriggered) {
-            if (mShortPressListener != null) {
-                mShortPressListener.cleanUpShortPress(WidgetCell.this);
-            }
-            mShortPressTriggered = false;
-        }
-    }
-
-    static void resetShortPressTarget() {
-        sShortpressTarget = null;
-    }
-
-    @Override
-    public boolean onTouchEvent(MotionEvent event) {
-        super.onTouchEvent(event);
-
-        switch (event.getAction()) {
-            case MotionEvent.ACTION_UP:
-                cleanUpShortPress();
-                break;
-            case MotionEvent.ACTION_DOWN:
-                checkForShortPress();
-                break;
-            case MotionEvent.ACTION_CANCEL:
-                cleanUpShortPress();
-                break;
-            case MotionEvent.ACTION_MOVE:
-                break;
-        }
-
-        // We eat up the touch events here, since the PagedView (which uses the same swiping
-        // touch code as Workspace previously) uses onInterceptTouchEvent() to determine when
-        // the user is scrolling between pages.  This means that if the pages themselves don't
-        // handle touch events, it gets forwarded up to PagedView itself, and it's own
-        // onTouchEvent() handling will prevent further intercept touch events from being called
-        // (it's the same view in that case).  This is not ideal, but to prevent more changes,
-        // we just always mark the touch event as handled.
-        return true;
-    }
-
     public void ensurePreview() {
         if (mActiveRequest != null) {
             return;
@@ -331,6 +219,16 @@
         return Math.min(size[0], info.spanX * cellWidth);
     }
 
+
+    private void deletePreview(boolean recycleImage) {
+        mWidgetImage.setImageDrawable(null);
+
+        if (mActiveRequest != null) {
+            mActiveRequest.cancel(recycleImage);
+            mActiveRequest = null;
+        }
+    }
+
     /**
      * Helper method to get the string info of the tag.
      */
diff --git a/src/com/android/launcher3/widget/WidgetHostViewLoader.java b/src/com/android/launcher3/widget/WidgetHostViewLoader.java
new file mode 100644
index 0000000..d654550
--- /dev/null
+++ b/src/com/android/launcher3/widget/WidgetHostViewLoader.java
@@ -0,0 +1,197 @@
+package com.android.launcher3.widget;
+
+import android.appwidget.AppWidgetHostView;
+import android.appwidget.AppWidgetManager;
+import android.content.Context;
+import android.graphics.Rect;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Handler;
+import android.util.Log;
+import android.view.View;
+
+import com.android.launcher3.AppWidgetResizeFrame;
+import com.android.launcher3.DragLayer;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.compat.AppWidgetManagerCompat;
+
+public class WidgetHostViewLoader {
+
+    private static final boolean DEBUG = false;
+    private static final String TAG = "WidgetHostViewLoader";
+
+    /* constants used for widget loading state. */
+    private static final int WIDGET_NO_CLEANUP_REQUIRED = -1;
+    private static final int WIDGET_PRELOAD_PENDING = 0;
+    private static final int WIDGET_BOUND = 1;
+    private static final int WIDGET_INFLATED = 2;
+
+    int mState = WIDGET_NO_CLEANUP_REQUIRED;
+
+    /* Runnables to handle inflation and binding. */
+    private Runnable mInflateWidgetRunnable = null;
+    private Runnable mBindWidgetRunnable = null;
+
+    /* Id of the widget being handled. */
+    int mWidgetLoadingId = -1;
+    PendingAddWidgetInfo mCreateWidgetInfo = null;
+
+    // TODO: technically, this class should not have to know the existence of the launcher.
+    private Launcher mLauncher;
+    private Handler mHandler;
+
+    public WidgetHostViewLoader(Launcher launcher) {
+        mLauncher = launcher;
+        mHandler = new Handler();
+    }
+
+    /**
+     * Start loading the widget.
+     */
+    public void load(View v) {
+        if (mCreateWidgetInfo != null) {
+            // Just in case the cleanup process wasn't properly executed.
+            finish(false);
+        }
+        boolean status = false;
+        if (v.getTag() instanceof PendingAddWidgetInfo) {
+            mCreateWidgetInfo = new PendingAddWidgetInfo((PendingAddWidgetInfo) v.getTag());
+            status = preloadWidget(v, mCreateWidgetInfo);
+        }
+        if (DEBUG) {
+            Log.d(TAG, String.format("load started on [state=%d, status=%s]", mState, status));
+        }
+    }
+
+
+    /**
+     * Clean up according to what the last known state was.
+     * @param widgetIdUsed   {@code true} if the widgetId was consumed which can happen only
+     *                       when view is fully inflated
+     */
+    public void finish(boolean widgetIdUsed) {
+        if (DEBUG) {
+            Log.d(TAG, String.format("cancel on state [%d] widgetId=[%d]",
+                    mState, mWidgetLoadingId));
+        }
+
+        // If the widget was not added, we may need to do further cleanup.
+        PendingAddWidgetInfo info = mCreateWidgetInfo;
+        mCreateWidgetInfo = null;
+
+        if (mState == WIDGET_PRELOAD_PENDING) {
+            // We never did any preloading, so just remove pending callbacks to do so
+            mHandler.removeCallbacks(mBindWidgetRunnable);
+            mHandler.removeCallbacks(mInflateWidgetRunnable);
+        } else if (mState == WIDGET_BOUND) {
+             // Delete the widget id which was allocated
+            if (mWidgetLoadingId != -1 && !info.isCustomWidget()) {
+                mLauncher.getAppWidgetHost().deleteAppWidgetId(mWidgetLoadingId);
+            }
+
+            // We never got around to inflating the widget, so remove the callback to do so.
+            mHandler.removeCallbacks(mInflateWidgetRunnable);
+        } else if (mState == WIDGET_INFLATED && !widgetIdUsed) {
+            // Delete the widget id which was allocated
+            if (mWidgetLoadingId != -1 && !info.isCustomWidget()) {
+                mLauncher.getAppWidgetHost().deleteAppWidgetId(mWidgetLoadingId);
+            }
+
+            // The widget was inflated and added to the DragLayer -- remove it.
+            AppWidgetHostView widget = info.boundWidget;
+            mLauncher.getDragLayer().removeView(widget);
+        }
+        setState(WIDGET_NO_CLEANUP_REQUIRED);
+        mWidgetLoadingId = -1;
+    }
+
+    private boolean preloadWidget(final View v, final PendingAddWidgetInfo info) {
+        final LauncherAppWidgetProviderInfo pInfo = info.info;
+
+        final Bundle options = pInfo.isCustomWidget ? null :
+                getDefaultOptionsForWidget(mLauncher, info);
+
+        // If there is a configuration activity, do not follow thru bound and inflate.
+        if (pInfo.configure != null) {
+            info.bindOptions = options;
+            return false;
+        }
+        setState(WIDGET_PRELOAD_PENDING);
+        mBindWidgetRunnable = new Runnable() {
+            @Override
+            public void run() {
+                if (pInfo.isCustomWidget) {
+                    setState(WIDGET_BOUND);
+                    return;
+                }
+
+                mWidgetLoadingId = mLauncher.getAppWidgetHost().allocateAppWidgetId();
+                if(AppWidgetManagerCompat.getInstance(mLauncher).bindAppWidgetIdIfAllowed(
+                        mWidgetLoadingId, pInfo, options)) {
+                    setState(WIDGET_BOUND);
+                }
+            }
+        };
+        mHandler.post(mBindWidgetRunnable);
+
+        mInflateWidgetRunnable = new Runnable() {
+            @Override
+            public void run() {
+                if (mState != WIDGET_BOUND) {
+                    return;
+                }
+                AppWidgetHostView hostView = mLauncher.getAppWidgetHost().createView(
+                        (Context) mLauncher, mWidgetLoadingId, pInfo);
+                info.boundWidget = hostView;
+                setState(WIDGET_INFLATED);
+                hostView.setVisibility(View.INVISIBLE);
+                int[] unScaledSize = mLauncher.getWorkspace().estimateItemSize(info, false);
+
+                // We want the first widget layout to be the correct size. This will be important
+                // for width size reporting to the AppWidgetManager.
+                DragLayer.LayoutParams lp = new DragLayer.LayoutParams(unScaledSize[0],
+                        unScaledSize[1]);
+                lp.x = lp.y = 0;
+                lp.customPosition = true;
+                hostView.setLayoutParams(lp);
+                mLauncher.getDragLayer().addView(hostView);
+                v.setTag(info);
+            }
+        };
+        mHandler.post(mInflateWidgetRunnable);
+        return true;
+    }
+
+    public static Bundle getDefaultOptionsForWidget(Launcher launcher, PendingAddWidgetInfo info) {
+        Bundle options = null;
+        Rect rect = new Rect();
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+            AppWidgetResizeFrame.getWidgetSizeRanges(launcher, info.spanX, info.spanY, rect);
+            Rect padding = AppWidgetHostView.getDefaultPaddingForWidget(launcher,
+                    info.componentName, null);
+
+            float density = launcher.getResources().getDisplayMetrics().density;
+            int xPaddingDips = (int) ((padding.left + padding.right) / density);
+            int yPaddingDips = (int) ((padding.top + padding.bottom) / density);
+
+            options = new Bundle();
+            options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH,
+                    rect.left - xPaddingDips);
+            options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT,
+                    rect.top - yPaddingDips);
+            options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH,
+                    rect.right - xPaddingDips);
+            options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT,
+                    rect.bottom - yPaddingDips);
+        }
+        return options;
+    }
+
+    private void setState(int state) {
+        if (DEBUG) {
+            Log.d(TAG, String.format("     state [%d -> %d]", mState, state));
+        }
+        mState = state;
+    }
+}
diff --git a/src/com/android/launcher3/widget/WidgetsContainerView.java b/src/com/android/launcher3/widget/WidgetsContainerView.java
index 292a5de..27a3ea1 100644
--- a/src/com/android/launcher3/widget/WidgetsContainerView.java
+++ b/src/com/android/launcher3/widget/WidgetsContainerView.java
@@ -30,6 +30,7 @@
 import android.view.View;
 import android.widget.FrameLayout;
 import android.widget.ImageView;
+import android.widget.Toast;
 
 import com.android.launcher3.CellLayout;
 import com.android.launcher3.DeleteDropTarget;
@@ -54,15 +55,17 @@
 /**
  * The widgets list view container.
  */
-public class WidgetsContainerView extends FrameLayout implements Insettable, View.OnTouchListener,
-        View.OnLongClickListener, DragSource{
+public class WidgetsContainerView extends FrameLayout implements Insettable,
+        View.OnLongClickListener, View.OnClickListener, DragSource{
 
-    private static final String TAG = "WidgetContainerView";
+    private static final String TAG = "WidgetsContainerView";
     private static final boolean DEBUG = false;
 
     /* {@link RecyclerView} will keep following # of views in cache, before recycling. */
     private static final int WIDGET_CACHE_SIZE = 2;
 
+    private static final int SPRING_MODE_DELAY_MS = 150;
+
     /* Global instances that are used inside this container. */
     private Launcher mLauncher;
     private DragController mDragController;
@@ -75,12 +78,13 @@
     private RecyclerView mView;
     private WidgetsListAdapter mAdapter;
 
-    /* Dragging related. */
-    private boolean mDraggingWidget = false;    // TODO(hyunyoungs): seems not needed? check!
-    private Point mLastTouchDownPos = new Point();
+    /* Touch handling related member variables. */
+    private Toast mWidgetInstructionToast;
 
     /* Rendering related. */
     private WidgetPreviewLoader mWidgetPreviewLoader;
+    private WidgetHostViewLoader mWidgetHostViewLoader;
+
     private Rect mPadding = new Rect();
 
     public WidgetsContainerView(Context context) {
@@ -95,8 +99,8 @@
         super(context, attrs, defStyleAttr);
         mLauncher = (Launcher) context;
         mDragController = mLauncher.getDragController();
-
-        mAdapter = new WidgetsListAdapter(context, this, mLauncher, this, mLauncher);
+        mWidgetHostViewLoader = new WidgetHostViewLoader(mLauncher);
+        mAdapter = new WidgetsListAdapter(context, this, this, mLauncher);
         mWidgets = new WidgetsModel(context, mAdapter);
         mAdapter.setWidgetsModel(mWidgets);
         mIconCache = (LauncherAppState.getInstance()).getIconCache();
@@ -147,11 +151,26 @@
     //
 
     @Override
+    public void onClick(View v) {
+        // When we have exited widget tray or are in transition, disregard clicks
+        if (!mLauncher.isWidgetsViewVisible()
+                || mLauncher.getWorkspace().isSwitchingState()
+                || !(v instanceof WidgetCell)) return;
+
+        // Let the user know that they have to long press to add a widget
+        if (mWidgetInstructionToast != null) {
+            mWidgetInstructionToast.cancel();
+        }
+        mWidgetInstructionToast = Toast.makeText(getContext(),R.string.long_press_widget_to_add,
+                Toast.LENGTH_SHORT);
+        mWidgetInstructionToast.show();
+    }
+
+    @Override
     public boolean onLongClick(View v) {
         if (DEBUG) {
             Log.d(TAG, String.format("onLonglick [v=%s]", v));
         }
-
         // Return early if this is not initiated from a touch
         if (!v.isInTouchMode()) return false;
         // When we have exited all apps or are in transition, disregard long clicks
@@ -161,7 +180,11 @@
         Log.d(TAG, String.format("onLonglick dragging enabled?.", v));
         if (!mLauncher.isDraggingEnabled()) return false;
 
-        return beginDragging(v);
+        boolean status = beginDragging(v);
+        if (status) {
+            mWidgetHostViewLoader.load(v);
+        }
+        return status;
     }
 
     private boolean beginDragging(View v) {
@@ -174,7 +197,7 @@
         }
 
         // We delay entering spring-loaded mode slightly to make sure the UI
-        // thready is free of any work.
+        // thread is free of any work.
         postDelayed(new Runnable() {
             @Override
             public void run() {
@@ -184,13 +207,12 @@
                     mLauncher.enterSpringLoadedDragMode();
                 }
             }
-        }, 150);
+        }, SPRING_MODE_DELAY_MS);
 
         return true;
     }
 
     private boolean beginDraggingWidget(WidgetCell v) {
-        mDraggingWidget = true;
         // Get the widget preview as the drag representation
         ImageView image = (ImageView) v.findViewById(R.id.widget_preview);
         PendingAddItemInfo createItemInfo = (PendingAddItemInfo) v.getTag();
@@ -198,7 +220,6 @@
         // If the ImageView doesn't have a drawable yet, the widget preview hasn't been loaded and
         // we abort the drag.
         if (image.getDrawable() == null) {
-            mDraggingWidget = false;
             return false;
         }
 
@@ -259,19 +280,6 @@
         return true;
     }
 
-    /*
-     * @see android.view.View.OnTouchListener#onTouch(android.view.View, android.view.MotionEvent)
-     */
-    @Override
-    public boolean onTouch(View v, MotionEvent ev) {
-        Log.d(TAG, String.format("onTouch [MotionEvent=%s]", ev));
-        if (ev.getAction() == MotionEvent.ACTION_DOWN ||
-                ev.getAction() == MotionEvent.ACTION_MOVE) {
-            mLastTouchDownPos.set((int) ev.getX(), (int) ev.getY());
-        }
-        return false;
-    }
-
     //
     // Drag related handling methods that implement {@link DragSource} interface.
     //
@@ -340,6 +348,10 @@
             }
             d.deferDragViewCleanupPostAnimation = false;
         }
+        //TODO(hyunyoungs): if drop fails, this call cleans up correctly.
+        // However, in rare corner case where drop succeeds but doesn't end up using the widget
+        // id created by the loader, this finish will leave dangling widget id.
+        mWidgetHostViewLoader.finish(success);
     }
 
     //
@@ -368,5 +380,4 @@
         }
         return mWidgetPreviewLoader;
     }
-
 }
\ No newline at end of file
diff --git a/src/com/android/launcher3/widget/WidgetsListAdapter.java b/src/com/android/launcher3/widget/WidgetsListAdapter.java
index afeb2d3..f6ab21e 100644
--- a/src/com/android/launcher3/widget/WidgetsListAdapter.java
+++ b/src/com/android/launcher3/widget/WidgetsListAdapter.java
@@ -56,20 +56,16 @@
     private WidgetsModel mWidgetsModel;
     private WidgetPreviewLoader mWidgetPreviewLoader;
 
-    private View.OnTouchListener mTouchListener;
     private View.OnClickListener mIconClickListener;
     private View.OnLongClickListener mIconLongClickListener;
 
-
     public WidgetsListAdapter(Context context,
-            View.OnTouchListener touchListener,
             View.OnClickListener iconClickListener,
             View.OnLongClickListener iconLongClickListener,
             Launcher launcher) {
         mLayoutInflater = LayoutInflater.from(context);
         mContext = context;
 
-        mTouchListener = touchListener;
         mIconClickListener = iconClickListener;
         mIconLongClickListener = iconLongClickListener;
 
@@ -109,7 +105,6 @@
                 // set up touch.
                 widget.setOnClickListener(mIconClickListener);
                 widget.setOnLongClickListener(mIconLongClickListener);
-                widget.setOnTouchListener(mTouchListener);
                 row.addView(widget);
             }
         } else if (diff < 0) {