New folder visualization, renaming, staggering reorder

Change-Id: I62963d225e6ea5d2ec9d8ebc8a6d73099f5d6c7f
diff --git a/src/com/android/launcher2/FolderIcon.java b/src/com/android/launcher2/FolderIcon.java
index 18b242b..db3dfe8 100644
--- a/src/com/android/launcher2/FolderIcon.java
+++ b/src/com/android/launcher2/FolderIcon.java
@@ -30,7 +30,8 @@
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
-import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
 import android.widget.TextView;
 
 import com.android.launcher.R;
@@ -42,39 +43,32 @@
 /**
  * An icon that can appear on in the workspace representing an {@link UserFolder}.
  */
-public class FolderIcon extends FrameLayout implements FolderListener {
+public class FolderIcon extends LinearLayout implements FolderListener {
     private Launcher mLauncher;
     Folder mFolder;
     FolderInfo mInfo;
 
     // The number of icons to display in the
-    private static final int NUM_ITEMS_IN_PREVIEW = 4;
+    private static final int NUM_ITEMS_IN_PREVIEW = 3;
     private static final int CONSUMPTION_ANIMATION_DURATION = 100;
 
     // The degree to which the inner ring grows when accepting drop
     private static final float INNER_RING_GROWTH_FACTOR = 0.1f;
 
-    // The degree to which the inner ring is scaled in its natural state
-    private static final float INNER_RING_BASELINE_SCALE = 1.0f;
-
-    // The degree to which the outer ring grows when accepting drop
-    private static final float OUTER_RING_BASELINE_SCALE = 0.7f;
-
     // The degree to which the outer ring is scaled in its natural state
-    private static final float OUTER_RING_GROWTH_FACTOR = 0.3f;
+    private static final float OUTER_RING_GROWTH_FACTOR = 0.4f;
 
     // The amount of vertical spread between items in the stack [0...1]
-    private static final float PERSPECTIVE_SHIFT_FACTOR = 0.3f;
+    private static final float PERSPECTIVE_SHIFT_FACTOR = 0.24f;
 
     // The degree to which the item in the back of the stack is scaled [0...1]
     // (0 means it's not scaled at all, 1 means it's scaled to nothing)
-    private static final float PERSPECTIVE_SCALE_FACTOR = 0.3f;
-
-    // The percentage of the FolderIcons view that will be dedicated to the items preview
-    private static final float SPACE_PERCENTAGE_FOR_ICONS = 0.8f;
+    private static final float PERSPECTIVE_SCALE_FACTOR = 0.35f;
 
     private int mOriginalWidth = -1;
     private int mOriginalHeight = -1;
+    private ImageView mPreviewBackground;
+    private BubbleTextView mFolderName;
 
     FolderRingAnimator mFolderRingAnimator = null;
 
@@ -98,9 +92,10 @@
 
         FolderIcon icon = (FolderIcon) LayoutInflater.from(launcher).inflate(resId, group, false);
 
-        final Resources resources = launcher.getResources();
-        Drawable d = iconCache.getFullResIcon(resources, R.drawable.portal_ring_inner_holo);
-        icon.setBackgroundDrawable(d);
+        icon.mFolderName = (BubbleTextView) icon.findViewById(R.id.folder_name);
+        icon.mFolderName.setText(folderInfo.title);
+        icon.mPreviewBackground = (ImageView) icon.findViewById(R.id.preview_background);
+
         icon.setTag(folderInfo);
         icon.setOnClickListener(launcher);
         icon.mInfo = folderInfo;
@@ -121,31 +116,36 @@
     public static class FolderRingAnimator {
         public int mFolderLocX;
         public int mFolderLocY;
-        public float mOuterRingScale;
-        public float mInnerRingScale;
+        public float mOuterRingSize;
+        public float mInnerRingSize;
         public FolderIcon mFolderIcon = null;
         private Launcher mLauncher;
         public Drawable mOuterRingDrawable = null;
         public Drawable mInnerRingDrawable = null;
         public static Drawable sSharedOuterRingDrawable = null;
         public static Drawable sSharedInnerRingDrawable = null;
+        public static int sPreviewSize = -1;
+        public static int sPreviewPadding = -1;
+
         private ValueAnimator mAcceptAnimator;
         private ValueAnimator mNeutralAnimator;
 
         public FolderRingAnimator(Launcher launcher, FolderIcon folderIcon) {
             mLauncher = launcher;
             mFolderIcon = folderIcon;
-            mOuterRingDrawable =
-                    launcher.getResources().getDrawable(R.drawable.portal_ring_outer_holo);
-            mInnerRingDrawable =
-                    launcher.getResources().getDrawable(R.drawable.portal_ring_inner_holo);
+            Resources res = launcher.getResources();
+            mOuterRingDrawable = res.getDrawable(R.drawable.portal_ring_outer_holo);
+            mInnerRingDrawable = res.getDrawable(R.drawable.portal_ring_inner_holo);
+
+            if (sPreviewSize < 0 || sPreviewPadding < 0) {
+                sPreviewSize = res.getDimensionPixelSize(R.dimen.folder_preview_size);
+                sPreviewPadding = res.getDimensionPixelSize(R.dimen.folder_preview_padding);
+            }
             if (sSharedOuterRingDrawable == null) {
-                sSharedOuterRingDrawable =
-                        launcher.getResources().getDrawable(R.drawable.portal_ring_outer_holo);
+                sSharedOuterRingDrawable = res.getDrawable(R.drawable.portal_ring_outer_holo);
             }
             if (sSharedInnerRingDrawable == null) {
-                sSharedInnerRingDrawable =
-                        launcher.getResources().getDrawable(R.drawable.portal_ring_inner_holo);
+                sSharedInnerRingDrawable = res.getDrawable(R.drawable.portal_ring_inner_holo);
             }
         }
 
@@ -164,8 +164,8 @@
             mAcceptAnimator.addUpdateListener(new AnimatorUpdateListener() {
                 public void onAnimationUpdate(ValueAnimator animation) {
                     final float percent = (Float) animation.getAnimatedValue();
-                    mOuterRingScale = OUTER_RING_BASELINE_SCALE + percent * OUTER_RING_GROWTH_FACTOR;
-                    mInnerRingScale = INNER_RING_BASELINE_SCALE + percent * INNER_RING_GROWTH_FACTOR;
+                    mOuterRingSize = (1 + percent * OUTER_RING_GROWTH_FACTOR) * sPreviewSize;
+                    mInnerRingSize = (1 + percent * INNER_RING_GROWTH_FACTOR) * sPreviewSize;
                     mLauncher.getWorkspace().invalidate();
                     if (mFolderIcon != null) {
                         mFolderIcon.invalidate();
@@ -176,7 +176,7 @@
                 @Override
                 public void onAnimationStart(Animator animation) {
                     if (mFolderIcon != null) {
-                        mFolderIcon.setBackgroundDrawable(null);
+                        mFolderIcon.mPreviewBackground.setVisibility(INVISIBLE);
                     }
                 }
             });
@@ -192,10 +192,8 @@
             mNeutralAnimator.addUpdateListener(new AnimatorUpdateListener() {
                 public void onAnimationUpdate(ValueAnimator animation) {
                     final float percent = (Float) animation.getAnimatedValue();
-                    mOuterRingScale = OUTER_RING_BASELINE_SCALE + OUTER_RING_GROWTH_FACTOR
-                            - percent * OUTER_RING_GROWTH_FACTOR;
-                    mInnerRingScale = INNER_RING_BASELINE_SCALE + INNER_RING_GROWTH_FACTOR
-                            - percent * INNER_RING_GROWTH_FACTOR;
+                    mOuterRingSize = (1 + (1 - percent) * OUTER_RING_GROWTH_FACTOR) * sPreviewSize;
+                    mInnerRingSize = (1 + (1 - percent) * INNER_RING_GROWTH_FACTOR) * sPreviewSize;
                     mLauncher.getWorkspace().invalidate();
                     if (mFolderIcon != null) {
                         mFolderIcon.invalidate();
@@ -206,7 +204,7 @@
                 @Override
                 public void onAnimationEnd(Animator animation) {
                     if (mFolderIcon != null) {
-                        mFolderIcon.setBackgroundDrawable(mInnerRingDrawable);
+                        mFolderIcon.mPreviewBackground.setVisibility(VISIBLE);
                     }
                     mLauncher.getWorkspace().hideFolderAccept(FolderRingAnimator.this);
                 }
@@ -220,12 +218,12 @@
             loc[1] = mFolderLocY;
         }
 
-        public float getOuterRingScale() {
-            return mOuterRingScale;
+        public float getOuterRingSize() {
+            return mOuterRingSize;
         }
 
-        public float getInnerRingScale() {
-            return mInnerRingScale;
+        public float getInnerRingSize() {
+            return mInnerRingSize;
         }
     }
 
@@ -258,7 +256,7 @@
         mLauncher.getWorkspace().getLocationInWindow(wsLocation);
 
         int x = tvLocation[0] - wsLocation[0] + getMeasuredWidth() / 2;
-        int y = tvLocation[1] - wsLocation[1] + getMeasuredHeight() / 2;
+        int y = tvLocation[1] - wsLocation[1] + FolderRingAnimator.sPreviewSize / 2;
         mFolderRingAnimator.setLocation(x, y);
     }
 
@@ -296,31 +294,38 @@
 
     @Override
     protected void dispatchDraw(Canvas canvas) {
+        super.dispatchDraw(canvas);
+
         if (mFolder == null) return;
         if (mFolder.getItemCount() == 0) return;
 
         canvas.save();
         TextView v = (TextView) mFolder.getItemAt(0);
         Drawable d = v.getCompoundDrawables()[1];
+        int intrinsicIconSize = d.getIntrinsicHeight();
 
         if (mOriginalWidth < 0 || mOriginalHeight < 0) {
             mOriginalWidth = getMeasuredWidth();
             mOriginalHeight = getMeasuredHeight();
         }
+        final int previewSize = FolderRingAnimator.sPreviewSize;
+        final int previewPadding = FolderRingAnimator.sPreviewPadding;
 
-        int unscaledHeight = (int) (d.getIntrinsicHeight() * (1 + PERSPECTIVE_SHIFT_FACTOR));
-        float baselineIconScale = SPACE_PERCENTAGE_FOR_ICONS / (unscaledHeight / (mOriginalHeight * 1.0f));
+        int halfAvailableSpace = (previewSize - 2 * previewPadding) / 2;
+        // cos(45) = 0.707  + ~= 0.1)
+        int availableSpace = (int) (halfAvailableSpace * (1 + 0.8f));
 
-        int baselineHeight = (int) (d.getIntrinsicHeight() * baselineIconScale);
-        int totalStackHeight = (int) (baselineHeight * (1 + PERSPECTIVE_SHIFT_FACTOR));
-        int baselineWidth = (int) (d.getIntrinsicWidth() * baselineIconScale);
-        float maxPerpectiveShift = baselineHeight * PERSPECTIVE_SHIFT_FACTOR;
+        int unscaledHeight = (int) (intrinsicIconSize * (1 + PERSPECTIVE_SHIFT_FACTOR));
+        float baselineIconScale = (1.0f * availableSpace / unscaledHeight);
+
+        int baselineSize = (int) (intrinsicIconSize * baselineIconScale);
+        float maxPerspectiveShift = baselineSize * PERSPECTIVE_SHIFT_FACTOR;
 
         ArrayList<View> items = mFolder.getItemsInReadingOrder(false);
         int firstItemIndex = Math.max(0, items.size() - NUM_ITEMS_IN_PREVIEW);
 
-        int xShift = (int) (mOriginalWidth - baselineWidth) / 2;
-        int yShift = (int) (mOriginalHeight - totalStackHeight) / 2;
+        int xShift = (mOriginalWidth - 2 * halfAvailableSpace) / 2;
+        int yShift = previewPadding;
         canvas.translate(xShift, yShift);
         for (int i = firstItemIndex; i < items.size(); i++) {
             int index = i - firstItemIndex;
@@ -328,10 +333,17 @@
 
             float r = (index * 1.0f) / (NUM_ITEMS_IN_PREVIEW - 1);
             float scale = (1 - PERSPECTIVE_SCALE_FACTOR * (1 - r));
-            r = (float) Math.pow(r, 2);
 
-            float transY = r * maxPerpectiveShift;
-            float transX = (1 - scale) * baselineWidth / 2.0f;
+            //r = (float) Math.pow(r, 2);
+
+            float offset = (1 - r) * maxPerspectiveShift;
+            float scaledSize = scale * baselineSize;
+            float scaleOffsetCorrection = (1 - scale) * baselineSize;
+
+            // We want to imagine our coordinates from the bottom left, growing up and to the
+            // right. This is natural for the x-axis, but for the y-axis, we have to invert things.
+            float transY = 2 * halfAvailableSpace - (offset + scaledSize + scaleOffsetCorrection);
+            float transX = offset + scaleOffsetCorrection;
 
             v = (TextView) items.get(i);
             d = v.getCompoundDrawables()[1];
@@ -342,10 +354,12 @@
 
             int overlayAlpha = (int) (80 * (1 - r));
             if (d != null) {
-                d.setBounds(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());
+                d.setBounds(0, 0, intrinsicIconSize, intrinsicIconSize);
+                d.setFilterBitmap(true);
                 d.setColorFilter(Color.argb(overlayAlpha, 0, 0, 0), PorterDuff.Mode.SRC_ATOP);
                 d.draw(canvas);
                 d.clearColorFilter();
+                d.setFilterBitmap(false);
             }
             canvas.restore();
         }
@@ -366,4 +380,10 @@
         invalidate();
         requestLayout();
     }
+
+    public void onTitleChanged(CharSequence title) {
+        mFolderName.setText(title);
+        mFolderName.invalidate();
+        mFolderName.requestLayout();
+    }
 }