Fixing preloaded widget not getting used for animation

> The preloaded widget was being set in a different instance of
PendingAddWidgetInfo and was never getting used for animation.

bug: 20699153
Change-Id: Iaec13640e49c66993b4695e4a52dc3a3a2133fb2
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 2ff6adc..9756727 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -2370,6 +2370,9 @@
         if (hostView != null) {
             appWidgetId = hostView.getAppWidgetId();
             addAppWidgetImpl(appWidgetId, info, hostView, info.info);
+
+            // Clear the boundWidget so that it doesn't get destroyed.
+            info.boundWidget = null;
         } else {
             // In this case, we either need to start an activity to get permission to bind
             // the widget, or we need to start an activity to configure the widget, or both.
diff --git a/src/com/android/launcher3/widget/PendingAddWidgetInfo.java b/src/com/android/launcher3/widget/PendingAddWidgetInfo.java
index 36cc2b1..88a6ca4 100644
--- a/src/com/android/launcher3/widget/PendingAddWidgetInfo.java
+++ b/src/com/android/launcher3/widget/PendingAddWidgetInfo.java
@@ -65,25 +65,6 @@
         return itemType == LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET;
     }
 
-    // Copy constructor
-    public PendingAddWidgetInfo(PendingAddWidgetInfo copy) {
-        minWidth = copy.minWidth;
-        minHeight = copy.minHeight;
-        minResizeWidth = copy.minResizeWidth;
-        minResizeHeight = copy.minResizeHeight;
-        previewImage = copy.previewImage;
-        icon = copy.icon;
-        info = copy.info;
-        boundWidget = copy.boundWidget;
-        componentName = copy.componentName;
-        itemType = copy.itemType;
-        spanX = copy.spanX;
-        spanY = copy.spanY;
-        minSpanX = copy.minSpanX;
-        minSpanY = copy.minSpanY;
-        bindOptions = copy.bindOptions == null ? null : (Bundle) copy.bindOptions.clone();
-    }
-
     @Override
     public String toString() {
         return String.format("PendingAddWidgetInfo package=%s, name=%s",
diff --git a/src/com/android/launcher3/widget/WidgetHostViewLoader.java b/src/com/android/launcher3/widget/WidgetHostViewLoader.java
index 8875879..30b3d58 100644
--- a/src/com/android/launcher3/widget/WidgetHostViewLoader.java
+++ b/src/com/android/launcher3/widget/WidgetHostViewLoader.java
@@ -7,148 +7,111 @@
 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.DragController.DragListener;
 import com.android.launcher3.DragLayer;
+import com.android.launcher3.DragSource;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.compat.AppWidgetManagerCompat;
 import com.android.launcher3.util.Thunk;
 
-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;
+public class WidgetHostViewLoader implements DragListener {
 
     /* Runnables to handle inflation and binding. */
-    private Runnable mInflateWidgetRunnable = null;
+    @Thunk 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.
     @Thunk Launcher mLauncher;
-    private Handler mHandler;
+    @Thunk Handler mHandler;
+    @Thunk final View mView;
+    @Thunk final PendingAddWidgetInfo mInfo;
 
-    public WidgetHostViewLoader(Launcher launcher) {
+    // Widget id generated for binding a widget host view or -1 for invalid id. The id is
+    // not is use as long as it is stored here and can be deleted safely. Once its used, this value
+    // to be set back to -1.
+    @Thunk int mWidgetLoadingId = -1;
+
+    public WidgetHostViewLoader(Launcher launcher, View view) {
         mLauncher = launcher;
         mHandler = new Handler();
+        mView = view;
+        mInfo = (PendingAddWidgetInfo) view.getTag();
+    }
+
+    @Override
+    public void onDragStart(DragSource source, Object info, int dragAction) { }
+
+    @Override
+    public void onDragEnd() {
+        // Cleanup up preloading state.
+        mLauncher.getDragController().removeDragListener(this);
+
+        mHandler.removeCallbacks(mBindWidgetRunnable);
+        mHandler.removeCallbacks(mInflateWidgetRunnable);
+
+        // Cleanup widget id
+        if (mWidgetLoadingId != -1) {
+            mLauncher.getAppWidgetHost().deleteAppWidgetId(mWidgetLoadingId);
+            mWidgetLoadingId = -1;
+        }
+
+        // The widget was inflated and added to the DragLayer -- remove it.
+        if (mInfo.boundWidget != null) {
+            mLauncher.getDragLayer().removeView(mInfo.boundWidget);
+            mLauncher.getAppWidgetHost().deleteAppWidgetId(mInfo.boundWidget.getAppWidgetId());
+            mInfo.boundWidget = null;
+        }
     }
 
     /**
-     * Start loading the widget.
+     * Start preloading the widget.
      */
-    public void load(View v) {
-        if (mCreateWidgetInfo != null) {
-            // Just in case the cleanup process wasn't properly executed.
-            finish(false);
+    public boolean preloadWidget() {
+        final LauncherAppWidgetProviderInfo pInfo = mInfo.info;
+
+        if (pInfo.isCustomWidget) {
+            return 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);
+        final Bundle options = getDefaultOptionsForWidget(mLauncher, mInfo);
 
         // If there is a configuration activity, do not follow thru bound and inflate.
         if (pInfo.configure != null) {
-            info.bindOptions = options;
+            mInfo.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);
+
+                    // Widget id bound. Inflate the widget.
+                    mHandler.post(mInflateWidgetRunnable);
                 }
             }
         };
-        mHandler.post(mBindWidgetRunnable);
 
         mInflateWidgetRunnable = new Runnable() {
             @Override
             public void run() {
-                if (mState != WIDGET_BOUND) {
+                if (mWidgetLoadingId == -1) {
                     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);
+                mInfo.boundWidget = hostView;
 
+                // We used up the widget Id in binding the above view.
+                mWidgetLoadingId = -1;
+
+                hostView.setVisibility(View.INVISIBLE);
+                int[] unScaledSize = mLauncher.getWorkspace().estimateItemSize(mInfo, 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],
@@ -157,10 +120,11 @@
                 lp.customPosition = true;
                 hostView.setLayoutParams(lp);
                 mLauncher.getDragLayer().addView(hostView);
-                v.setTag(info);
+                mView.setTag(mInfo);
             }
         };
-        mHandler.post(mInflateWidgetRunnable);
+
+        mHandler.post(mBindWidgetRunnable);
         return true;
     }
 
@@ -188,11 +152,4 @@
         }
         return options;
     }
-
-    @Thunk 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 8d04be5..aa139cb 100644
--- a/src/com/android/launcher3/widget/WidgetsContainerView.java
+++ b/src/com/android/launcher3/widget/WidgetsContainerView.java
@@ -21,7 +21,6 @@
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.support.v7.widget.LinearLayoutManager;
-import android.support.v7.widget.RecyclerView;
 import android.support.v7.widget.RecyclerView.State;
 import android.util.AttributeSet;
 import android.util.Log;
@@ -74,7 +73,6 @@
 
     /* Rendering related. */
     private WidgetPreviewLoader mWidgetPreviewLoader;
-    private WidgetHostViewLoader mWidgetHostViewLoader;
 
     private Rect mPadding = new Rect();
 
@@ -90,7 +88,6 @@
         super(context, attrs, defStyleAttr);
         mLauncher = (Launcher) context;
         mDragController = mLauncher.getDragController();
-        mWidgetHostViewLoader = new WidgetHostViewLoader(mLauncher);
         mAdapter = new WidgetsListAdapter(context, this, this, mLauncher);
         mIconCache = (LauncherAppState.getInstance()).getIconCache();
 
@@ -170,8 +167,13 @@
         if (!mLauncher.isDraggingEnabled()) return false;
 
         boolean status = beginDragging(v);
-        if (status) {
-            mWidgetHostViewLoader.load(v);
+        if (status && v.getTag() instanceof PendingAddWidgetInfo) {
+            WidgetHostViewLoader hostLoader = new WidgetHostViewLoader(mLauncher, v);
+            boolean preloadStatus = hostLoader.preloadWidget();
+            if (DEBUG) {
+                Log.d(TAG, String.format("preloading widget [status=%s]", preloadStatus));
+            }
+            mLauncher.getDragController().addDragListener(hostLoader);
         }
         return status;
     }
@@ -325,10 +327,6 @@
             }
             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);
     }
 
     //