updated Workspace thumbnail behavior

- in All Apps mode, fade thumbnails completely
- while dragging, have thumbnails re-appear
- while dragging, give screen thumbnails that can accept drops a different appearance (green outline instead of blue)

Change-Id: I72ddf8a0f1947d35ef11514b7d4eea9ae5eee6e2
diff --git a/src/com/android/launcher2/AllAppsPagedView.java b/src/com/android/launcher2/AllAppsPagedView.java
index b392959..cbb46e2 100644
--- a/src/com/android/launcher2/AllAppsPagedView.java
+++ b/src/com/android/launcher2/AllAppsPagedView.java
@@ -224,6 +224,7 @@
         ApplicationInfo app = (ApplicationInfo) v.getTag();
         app = new ApplicationInfo(app);
 
+        mLauncher.getWorkspace().onDragStartedWithItemSpans(1, 1);
         mDragController.startDrag(v, this, app, DragController.DRAG_ACTION_COPY);
         return true;
     }
@@ -234,6 +235,7 @@
         if (target != this) {
             endChoiceMode();
         }
+        mLauncher.getWorkspace().onDragStopped();
     }
 
     @Override
diff --git a/src/com/android/launcher2/CellLayout.java b/src/com/android/launcher2/CellLayout.java
index 369f0d2..d4a12fb 100644
--- a/src/com/android/launcher2/CellLayout.java
+++ b/src/com/android/launcher2/CellLayout.java
@@ -78,6 +78,8 @@
     private Drawable mBackgroundMini;
     private Drawable mBackgroundMiniHover;
     private Drawable mBackgroundHover;
+    private Drawable mBackgroundMiniAcceptsDrops;
+    private boolean mAcceptsDrops;
 
     // If we're actively dragging something over this screen, mHover is true
     private boolean mHover = false;
@@ -155,6 +157,9 @@
             mBackgroundMiniHover.setFilterBitmap(true);
             mBackgroundHover = res.getDrawable(R.drawable.home_screen_bg_hover);
             mBackgroundHover.setFilterBitmap(true);
+            mBackgroundMiniAcceptsDrops = res.getDrawable(
+                    R.drawable.mini_home_screen_bg_accepts_drops);
+            mBackgroundMiniAcceptsDrops.setFilterBitmap(true);
         }
 
         // Initialize the data structures used for the drag visualization.
@@ -202,9 +207,9 @@
 
     public void setHover(boolean value) {
         if (mHover != value) {
+            mHover = value;
             invalidate();
         }
-        mHover = value;
     }
 
     public void drawChildren(Canvas canvas) {
@@ -213,10 +218,15 @@
 
     @Override
     protected void onDraw(Canvas canvas) {
+        // When we're large, we are either drawn in a "hover" state (ie when dragging an item to
+        // a neighboring page) or with just a normal background (if backgroundAlpha > 0.0f)
+        // When we're small, we are either drawn normally or in the "accepts drops" state (during
+        // a drag). However, we also drag the mini hover background *over* one of those two
+        // backgrounds
         if (mBackgroundAlpha > 0.0f) {
             Drawable bg;
             if (getScaleX() < 0.5f) {
-                bg = mHover ? mBackgroundMiniHover : mBackgroundMini;
+                bg = mAcceptsDrops ? mBackgroundMiniAcceptsDrops : mBackgroundMini;
             } else {
                 bg = mHover ? mBackgroundHover : mBackground;
             }
@@ -224,6 +234,10 @@
                 bg.setAlpha((int) (mBackgroundAlpha * 255));
                 bg.draw(canvas);
             }
+            if (mHover && getScaleX() < 0.5f) {
+                mBackgroundMiniHover.setAlpha((int) (mBackgroundAlpha * 255));
+                mBackgroundMiniHover.draw(canvas);
+            }
         }
 
         if (mCrosshairsVisibility > 0.0f) {
@@ -330,6 +344,16 @@
         }
         return false;
     }
+    public void setAcceptsDrops(boolean acceptsDrops) {
+        if (mAcceptsDrops != acceptsDrops) {
+            mAcceptsDrops = acceptsDrops;
+            invalidate();
+        }
+    }
+
+    public boolean getAcceptsDrops() {
+        return mAcceptsDrops;
+    }
 
     @Override
     public void removeAllViews() {
@@ -643,6 +667,9 @@
         if (mBackgroundMini != null) {
             mBackgroundMini.setBounds(0, 0, w, h);
         }
+        if (mBackgroundMiniAcceptsDrops != null) {
+            mBackgroundMiniAcceptsDrops.setBounds(0, 0, w, h);
+        }
     }
 
     @Override
diff --git a/src/com/android/launcher2/CustomizePagedView.java b/src/com/android/launcher2/CustomizePagedView.java
index f3de74b..93aa109 100644
--- a/src/com/android/launcher2/CustomizePagedView.java
+++ b/src/com/android/launcher2/CustomizePagedView.java
@@ -321,7 +321,7 @@
 
     @Override
     public void onDropCompleted(View target, boolean success) {
-        // do nothing
+        mLauncher.getWorkspace().onDragStopped();
     }
 
     @Override
@@ -431,9 +431,11 @@
                     Bitmap.Config.ARGB_8888);
             Canvas c = new Canvas(b);
             icon.draw(c);
+            PendingAddWidgetInfo createWidgetInfo = (PendingAddWidgetInfo) v.getTag();
 
-            createItemInfo = (PendingAddItemInfo) v.getTag();
-            mDragController.startDrag(v, b, this, createItemInfo, DragController.DRAG_ACTION_COPY,
+            mLauncher.getWorkspace().onDragStartedWithItemMinSize(
+                    createWidgetInfo.minWidth, createWidgetInfo.minHeight);
+            mDragController.startDrag(v, b, this, createWidgetInfo, DragController.DRAG_ACTION_COPY,
                     null);
 
             // Cleanup the icon
@@ -450,11 +452,13 @@
                 mDragController.startDrag(
                         v, this, createItemInfo, DragController.DRAG_ACTION_COPY, null);
             }
+            mLauncher.getWorkspace().onDragStartedWithItemSpans(1, 1);
             return true;
         case ShortcutCustomization:
             createItemInfo = (PendingAddItemInfo) v.getTag();
             mDragController.startDrag(
                     v, this, createItemInfo, DragController.DRAG_ACTION_COPY, null);
+            mLauncher.getWorkspace().onDragStartedWithItemSpans(1, 1);
             return true;
         case ApplicationCustomization:
             // Pick up the application for dropping
@@ -462,6 +466,7 @@
             app = new ApplicationInfo(app);
 
             mDragController.startDrag(v, this, app, DragController.DRAG_ACTION_COPY);
+            mLauncher.getWorkspace().onDragStartedWithItemSpans(1, 1);
             return true;
         }
         return false;
@@ -613,9 +618,11 @@
         layout.removeAllViews();
         for (int i = 0; i < count; ++i) {
             AppWidgetProviderInfo info = (AppWidgetProviderInfo) list.get(i);
-            PendingAddItemInfo createItemInfo = new PendingAddItemInfo();
+            PendingAddWidgetInfo createItemInfo = new PendingAddWidgetInfo();
             createItemInfo.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET;
             createItemInfo.componentName = info.provider;
+            createItemInfo.minWidth = info.minWidth;
+            createItemInfo.minHeight = info.minHeight;
 
             LinearLayout l = (LinearLayout) mInflater.inflate(
                     R.layout.customize_paged_view_widget, layout, false);
diff --git a/src/com/android/launcher2/PendingAddItemInfo.java b/src/com/android/launcher2/PendingAddItemInfo.java
index 23e2330..904f512 100644
--- a/src/com/android/launcher2/PendingAddItemInfo.java
+++ b/src/com/android/launcher2/PendingAddItemInfo.java
@@ -26,4 +26,9 @@
      * The component that will be created.
      */
     ComponentName componentName;
+}
+
+class PendingAddWidgetInfo extends PendingAddItemInfo {
+    int minWidth;
+    int minHeight;
 }
\ No newline at end of file
diff --git a/src/com/android/launcher2/Workspace.java b/src/com/android/launcher2/Workspace.java
index 97c54c4..a22a466 100644
--- a/src/com/android/launcher2/Workspace.java
+++ b/src/com/android/launcher2/Workspace.java
@@ -76,13 +76,11 @@
     private static final int BACKGROUND_FADE_OUT_DURATION = 300;
     private static final int BACKGROUND_FADE_IN_DURATION = 100;
 
-    // These animators are used to fade the
-    private ObjectAnimator<Float> mBackgroundFadeIn;
-    private ObjectAnimator<Float> mBackgroundFadeOut;
+    // These animators are used to fade the background
+    private ObjectAnimator<Float> mBackgroundFadeInAnimation;
+    private ObjectAnimator<Float> mBackgroundFadeOutAnimation;
     private float mBackgroundAlpha = 0;
 
-    private enum ShrinkPosition { SHRINK_TO_TOP, SHRINK_TO_MIDDLE, SHRINK_TO_BOTTOM };
-
     private final WallpaperManager mWallpaperManager;
 
     private int mDefaultPage;
@@ -132,6 +130,9 @@
     private boolean mIsSmall = false;
     private boolean mIsInUnshrinkAnimation = false;
     private AnimatorListener mUnshrinkAnimationListener;
+    private enum ShrinkPosition {
+        SHRINK_TO_TOP, SHRINK_TO_MIDDLE, SHRINK_TO_BOTTOM_HIDDEN, SHRINK_TO_BOTTOM_VISIBLE };
+    private ShrinkPosition mShrunkenState;
 
     private boolean mInScrollArea = false;
 
@@ -465,20 +466,24 @@
     }
 
     public void showOutlines() {
-        if (mBackgroundFadeOut != null) mBackgroundFadeOut.cancel();
-        if (mBackgroundFadeIn != null) mBackgroundFadeIn.cancel();
-        mBackgroundFadeIn = new ObjectAnimator<Float>(BACKGROUND_FADE_IN_DURATION, this,
-                        new PropertyValuesHolder<Float>("backgroundAlpha", 1.0f));
-        mBackgroundFadeIn.start();
+        if (!mIsSmall && !mIsInUnshrinkAnimation) {
+            if (mBackgroundFadeOutAnimation != null) mBackgroundFadeOutAnimation.cancel();
+            if (mBackgroundFadeInAnimation != null) mBackgroundFadeInAnimation.cancel();
+            mBackgroundFadeInAnimation = new ObjectAnimator<Float>(BACKGROUND_FADE_IN_DURATION,
+                    this, new PropertyValuesHolder<Float>("backgroundAlpha", 1.0f));
+            mBackgroundFadeInAnimation.start();
+        }
     }
 
     public void hideOutlines() {
-        if (mBackgroundFadeIn != null) mBackgroundFadeIn.cancel();
-        if (mBackgroundFadeOut != null) mBackgroundFadeOut.cancel();
-        mBackgroundFadeOut = new ObjectAnimator<Float>(BACKGROUND_FADE_OUT_DURATION, this,
-                        new PropertyValuesHolder<Float>("backgroundAlpha", 0.0f));
-        mBackgroundFadeOut.setStartDelay(BACKGROUND_FADE_OUT_DELAY);
-        mBackgroundFadeOut.start();
+        if (!mIsSmall && !mIsInUnshrinkAnimation) {
+            if (mBackgroundFadeInAnimation != null) mBackgroundFadeInAnimation.cancel();
+            if (mBackgroundFadeOutAnimation != null) mBackgroundFadeOutAnimation.cancel();
+            mBackgroundFadeOutAnimation = new ObjectAnimator<Float>(BACKGROUND_FADE_OUT_DURATION,
+                    this, new PropertyValuesHolder<Float>("backgroundAlpha", 0.0f));
+            mBackgroundFadeOutAnimation.setStartDelay(BACKGROUND_FADE_OUT_DELAY);
+            mBackgroundFadeOutAnimation.start();
+        }
     }
 
     public void setBackgroundAlpha(float alpha) {
@@ -624,7 +629,7 @@
             if (!mScroller.isFinished()) {
                 mScroller.abortAnimation();
             }
-            snapToPage(mCurrentPage);
+            setCurrentPage(mCurrentPage);
             return false; // We don't want the events.  Let them fall through to the all apps view.
         }
 
@@ -655,7 +660,7 @@
             // to get our width so we can layout the mini-screen views correctly
             mWaitingToShrinkToBottom = true;
         } else {
-            shrink(ShrinkPosition.SHRINK_TO_BOTTOM, animated);
+            shrink(ShrinkPosition.SHRINK_TO_BOTTOM_HIDDEN, animated);
         }
     }
 
@@ -674,6 +679,12 @@
     // we use this to shrink the workspace for the all apps view and the customize view
     private void shrink(ShrinkPosition shrinkPosition, boolean animated) {
         mIsSmall = true;
+        mShrunkenState = shrinkPosition;
+
+        // Stop any scrolling, move to the current page right away
+        setCurrentPage(mCurrentPage);
+        updateWhichPagesAcceptDrops(mShrunkenState);
+
         // we intercept and reject all touch events when we're small, so be sure to reset the state
         mTouchState = TOUCH_STATE_REST;
         mActivePointerId = INVALID_POINTER;
@@ -694,10 +705,20 @@
         float totalWidth = screenCount * scaledPageWidth + (screenCount - 1) * extraScaledSpacing;
 
         float newY = getResources().getDimension(R.dimen.smallScreenVerticalMargin);
-        if (shrinkPosition == ShrinkPosition.SHRINK_TO_BOTTOM) {
+        float finalAlpha = 1.0f;
+        float extraShrinkFactor = 1.0f;
+        if (shrinkPosition == ShrinkPosition.SHRINK_TO_BOTTOM_VISIBLE) {
+             newY = screenHeight - newY - scaledPageHeight;
+        } else if (shrinkPosition == ShrinkPosition.SHRINK_TO_BOTTOM_HIDDEN) {
+
+            // We shrink and disappear to nothing in the case of all apps
+            // (which is when we shrink to the bottom)
             newY = screenHeight - newY - scaledPageHeight;
+            finalAlpha = 0.0f;
+            extraShrinkFactor = 0.1f;
         } else if (shrinkPosition == ShrinkPosition.SHRINK_TO_MIDDLE) {
-            newY = screenHeight / 2 - scaledPageHeight / 2;
+             newY = screenHeight / 2 - scaledPageHeight / 2;
+             finalAlpha = 1.0f;
         }
 
         // We animate all the screens to the centered position in workspace
@@ -722,10 +743,12 @@
                 new ObjectAnimator<Float>(duration, cl,
                         new PropertyValuesHolder<Float>("x", newX),
                         new PropertyValuesHolder<Float>("y", newY),
-                        new PropertyValuesHolder<Float>("scaleX", SHRINK_FACTOR * rotationScaleX),
-                        new PropertyValuesHolder<Float>("scaleY", SHRINK_FACTOR * rotationScaleY),
-                        new PropertyValuesHolder<Float>("backgroundAlpha", 1.0f),
-                        new PropertyValuesHolder<Float>("alpha", 1.0f),
+                        new PropertyValuesHolder<Float>("scaleX",
+                                SHRINK_FACTOR * rotationScaleX * extraShrinkFactor),
+                        new PropertyValuesHolder<Float>("scaleY",
+                                SHRINK_FACTOR * rotationScaleY * extraShrinkFactor),
+                        new PropertyValuesHolder<Float>("backgroundAlpha", finalAlpha),
+                        new PropertyValuesHolder<Float>("alpha", finalAlpha),
                         new PropertyValuesHolder<Float>("rotationY", rotation)).start();
             } else {
                 cl.setX((int)newX);
@@ -733,7 +756,7 @@
                 cl.setScaleX(SHRINK_FACTOR * rotationScaleX);
                 cl.setScaleY(SHRINK_FACTOR * rotationScaleY);
                 cl.setBackgroundAlpha(1.0f);
-                cl.setAlpha(0.0f);
+                cl.setAlpha(finalAlpha);
                 cl.setRotationY(rotation);
             }
             // increment newX for the next screen
@@ -742,6 +765,78 @@
         setChildrenDrawnWithCacheEnabled(true);
     }
 
+
+    private void updateWhichPagesAcceptDrops(ShrinkPosition state) {
+        updateWhichPagesAcceptDropsHelper(state, false, 1, 1);
+    }
+
+
+    private void updateWhichPagesAcceptDropsDuringDrag(ShrinkPosition state, int spanX, int spanY) {
+        updateWhichPagesAcceptDropsHelper(state, true, spanX, spanY);
+    }
+
+    private void updateWhichPagesAcceptDropsHelper(
+            ShrinkPosition state, boolean isDragHappening, int spanX, int spanY) {
+        final int screenCount = getChildCount();
+        for (int i = 0; i < screenCount; i++) {
+            CellLayout cl = (CellLayout) getChildAt(i);
+
+            switch (state) {
+                case SHRINK_TO_TOP:
+                    if (!isDragHappening) {
+                        boolean showDropHighlight = i == mCurrentPage;
+                        cl.setAcceptsDrops(showDropHighlight);
+                        break;
+                    }
+                    // otherwise, fall through below and mark non-full screens as accepting drops
+                case SHRINK_TO_BOTTOM_HIDDEN:
+                case SHRINK_TO_BOTTOM_VISIBLE:
+                    if (!isDragHappening) {
+                        // even if a drag isn't happening, we don't want to show a screen as
+                        // accepting drops if it doesn't have at least one free cell
+                        spanX = 1;
+                        spanY = 1;
+                    }
+                    // the page accepts drops if we can find at least one empty spot
+                    cl.setAcceptsDrops(cl.findCellForSpan(null, spanX, spanY));
+                    break;
+                default:
+                     throw new RuntimeException(
+                             "updateWhichPagesAcceptDropsHelper passed an unhandled ShrinkPosition");
+            }
+        }
+    }
+
+    /*
+     *
+     * We call these methods (onDragStartedWithItemSpans/onDragStartedWithItemMinSize) whenever we
+     * start a drag in Launcher, regardless of whether the drag has ever entered the Workspace
+     *
+     * These methods mark the appropriate pages as accepting drops (which alters their visual
+     * appearance) and, if the pages are hidden, makes them visible.
+     *
+     */
+    public void onDragStartedWithItemSpans(int spanX, int spanY) {
+        updateWhichPagesAcceptDropsDuringDrag(mShrunkenState, spanX, spanY);
+        if (mShrunkenState == ShrinkPosition.SHRINK_TO_BOTTOM_HIDDEN) {
+            shrink(ShrinkPosition.SHRINK_TO_BOTTOM_VISIBLE, true);
+        }
+    }
+
+    public void onDragStartedWithItemMinSize(int minWidth, int minHeight) {
+        int[] spanXY = CellLayout.rectToCell(getResources(), minWidth, minHeight, null);
+        onDragStartedWithItemSpans(spanXY[0], spanXY[1]);
+    }
+
+    // we call this method whenever a drag and drop in Launcher finishes, even if Workspace was
+    // never dragged over
+    public void onDragStopped() {
+        updateWhichPagesAcceptDrops(mShrunkenState);
+        if (mShrunkenState == ShrinkPosition.SHRINK_TO_BOTTOM_VISIBLE) {
+            shrink(ShrinkPosition.SHRINK_TO_BOTTOM_HIDDEN, true);
+        }
+    }
+
     // We call this when we trigger an unshrink by clicking on the CellLayout cl
     public void unshrink(CellLayout clThatWasClicked) {
         int newCurrentPage = mCurrentPage;