New folder visualization, renaming, staggering reorder

Change-Id: I62963d225e6ea5d2ec9d8ebc8a6d73099f5d6c7f
diff --git a/src/com/android/launcher2/Folder.java b/src/com/android/launcher2/Folder.java
index d81183c..960fa55 100644
--- a/src/com/android/launcher2/Folder.java
+++ b/src/com/android/launcher2/Folder.java
@@ -23,16 +23,22 @@
 import android.animation.ValueAnimator;
 import android.animation.ValueAnimator.AnimatorUpdateListener;
 import android.content.Context;
-import android.graphics.Color;
+import android.content.res.Resources;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
+import android.view.ActionMode;
+import android.view.KeyEvent;
 import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.view.animation.AccelerateInterpolator;
 import android.view.animation.DecelerateInterpolator;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputMethodManager;
 import android.widget.AdapterView;
 import android.widget.LinearLayout;
 import android.widget.TextView;
@@ -48,7 +54,8 @@
  * Represents a set of icons chosen by the user or generated by the system.
  */
 public class Folder extends LinearLayout implements DragSource, OnItemLongClickListener,
-        OnItemClickListener, OnClickListener, View.OnLongClickListener, DropTarget, FolderListener {
+        OnItemClickListener, OnClickListener, View.OnLongClickListener, DropTarget, FolderListener,
+        TextView.OnEditorActionListener {
 
     protected DragController mDragController;
 
@@ -90,6 +97,11 @@
     private int[] mEmptyCell = new int[2];
     private Alarm mReorderAlarm = new Alarm();
     private Alarm mOnExitAlarm = new Alarm();
+    private TextView mFolderName;
+    private int mFolderNameHeight;
+
+    private boolean mIsEditingName = false;
+    private InputMethodManager mInputMethodManager;
 
     /**
      * Used to inflate the Workspace from XML.
@@ -102,10 +114,14 @@
         setAlwaysDrawnWithCacheEnabled(false);
         mInflater = LayoutInflater.from(context);
         mIconCache = ((LauncherApplication)context.getApplicationContext()).getIconCache();
-        mExpandDuration = getResources().getInteger(R.integer.config_folderAnimDuration);
-
         mMaxCountX = LauncherModel.getCellCountX() - 1;
         mMaxCountY = LauncherModel.getCellCountY() - 1;
+
+        mInputMethodManager = (InputMethodManager)
+                mContext.getSystemService(Context.INPUT_METHOD_SERVICE);
+
+        Resources res = getResources();
+        mExpandDuration = res.getInteger(R.integer.config_folderAnimDuration);
     }
 
     @Override
@@ -114,8 +130,37 @@
         mContent = (CellLayout) findViewById(R.id.folder_content);
         mContent.setGridSize(0, 0);
         mContent.enableHardwareLayers();
+        mFolderName = (TextView) findViewById(R.id.folder_name);
+
+        // We find out how tall the text view wants to be (it is set to wrap_content), so that
+        // we can allocate the appropriate amount of space for it.
+        int measureSpec = MeasureSpec.UNSPECIFIED;
+        mFolderName.measure(measureSpec, measureSpec);
+        mFolderNameHeight = mFolderName.getMeasuredHeight();
+
+        // We disable action mode for now since it messes up the view on phones
+        mFolderName.setCustomSelectionActionModeCallback(mActionModeCallback);
+        mFolderName.setCursorVisible(false);
+        mFolderName.setOnEditorActionListener(this);
     }
 
+    private ActionMode.Callback mActionModeCallback = new ActionMode.Callback() {
+        public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
+            return false;
+        }
+
+        public boolean onCreateActionMode(ActionMode mode, Menu menu) {
+            return false;
+        }
+
+        public void onDestroyActionMode(ActionMode mode) {
+        }
+
+        public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
+            return false;
+        }
+    };
+
     public void onItemClick(AdapterView parent, View v, int position, long id) {
         ShortcutInfo app = (ShortcutInfo) parent.getItemAtPosition(position);
         int[] pos = new int[2];
@@ -138,6 +183,17 @@
         }
     }
 
+    private Rect mHitRect = new Rect();
+    public boolean onInterceptTouchEvent(MotionEvent ev) {
+        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+            mFolderName.getHitRect(mHitRect);
+            if (mHitRect.contains((int) ev.getX(), (int) ev.getY()) && !mIsEditingName) {
+                startEditingFolderName();
+            }
+        }
+        return false;
+    }
+
     public boolean onLongClick(View v) {
         Object tag = v.getTag();
         if (tag instanceof ShortcutInfo) {
@@ -158,13 +214,44 @@
             mCurrentDragView = v;
             mContent.removeView(mCurrentDragView);
             mInfo.remove(item);
-        } else {
-            mLauncher.closeFolder(this);
-            mLauncher.showRenameDialog(mInfo);
         }
         return true;
     }
 
+    public boolean isEditingName() {
+        return mIsEditingName;
+    }
+
+    public void startEditingFolderName() {
+        mFolderName.setCursorVisible(true);
+        mIsEditingName = true;
+    }
+
+    public void dismissEditingName() {
+        mInputMethodManager.hideSoftInputFromWindow(getWindowToken(), 0);
+        doneEditingFolderName(true);
+    }
+
+    public void doneEditingFolderName(boolean commit) {
+        mInfo.setTitle(mFolderName.getText());
+        LauncherModel.updateItemInDatabase(mLauncher, mInfo);
+        mFolderName.setCursorVisible(false);
+        mFolderName.clearFocus();
+        mIsEditingName = false;
+    }
+
+    public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
+        if (actionId == EditorInfo.IME_ACTION_DONE) {
+            dismissEditingName();
+            return true;
+        }
+        return false;
+    }
+
+    public View getEditTextRegion() {
+        return mFolderName;
+    }
+
     public Drawable getDragDrawable() {
         return mIconDrawable;
     }
@@ -216,12 +303,13 @@
         // forcing a layout
         // TODO: find out if this is still necessary
         mContent.requestLayout();
-        requestFocus();
     }
 
     void onClose() {
-        final Workspace workspace = mLauncher.getWorkspace();
-        workspace.getChildAt(workspace.getCurrentPage()).requestFocus();
+        CellLayoutChildren clc = (CellLayoutChildren) getParent();
+        final CellLayout cellLayout = (CellLayout) clc.getParent();
+        cellLayout.removeViewWithoutMarkingCells(Folder.this);
+        clearFocus();
     }
 
     void bind(FolderInfo info) {
@@ -234,6 +322,7 @@
         }
         mItemsInvalidated = true;
         mInfo.addListener(this);
+        mFolderName.setText(mInfo.title);
     }
 
     /**
@@ -322,10 +411,7 @@
     public void animateClosed() {
         if (!(getParent() instanceof CellLayoutChildren)) return;
 
-        CellLayoutChildren clc = (CellLayoutChildren) getParent();
-        final CellLayout cellLayout = (CellLayout) clc.getParent();
         ObjectAnimator oa;
-
         if (mMode == PARTIAL_GROW) {
             PropertyValuesHolder alpha = PropertyValuesHolder.ofFloat("alpha", 0);
             PropertyValuesHolder scaleX = PropertyValuesHolder.ofFloat("scaleX", 0.9f);
@@ -356,8 +442,8 @@
         oa.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationEnd(Animator animation) {
+                onClose();
                 onCloseComplete();
-                cellLayout.removeViewWithoutMarkingCells(Folder.this);
                 mState = STATE_SMALL;
             }
             @Override
@@ -438,6 +524,8 @@
         int startX;
         int endX;
         int startY;
+        int delay = 0;
+        float delayAmount = 30;
         if (readingOrderGreaterThan(target, empty)) {
             wrap = empty[0] >= mContent.getCountX() - 1;
             startY = wrap ? empty[1] + 1 : empty[1];
@@ -447,9 +535,11 @@
                 for (int x = startX; x <= endX; x++) {
                     View v = mContent.getChildAt(x,y);
                     if (mContent.animateChildToPosition(v, empty[0], empty[1],
-                            REORDER_ANIMATION_DURATION)) {
+                            REORDER_ANIMATION_DURATION, delay)) {
                         empty[0] = x;
                         empty[1] = y;
+                        delay += delayAmount;
+                        delayAmount *= 0.9;
                     }
                 }
             }
@@ -462,9 +552,11 @@
                 for (int x = startX; x >= endX; x--) {
                     View v = mContent.getChildAt(x,y);
                     if (mContent.animateChildToPosition(v, empty[0], empty[1],
-                            REORDER_ANIMATION_DURATION)) {
+                            REORDER_ANIMATION_DURATION, delay)) {
                         empty[0] = x;
                         empty[1] = y;
+                        delay += delayAmount;
+                        delayAmount *= 0.9;
                     }
                 }
             }
@@ -591,7 +683,9 @@
         CellLayout.LayoutParams lp = (CellLayout.LayoutParams) getLayoutParams();
 
         int width = getPaddingLeft() + getPaddingRight() + mContent.getDesiredWidth();
-        int height = getPaddingTop() + getPaddingBottom() + mContent.getDesiredHeight();
+        // Technically there is no padding at the bottom, but we add space equal to the padding
+        // and have to account for that here.
+        int height = getPaddingTop() + mContent.getDesiredHeight() + mFolderNameHeight;
 
         int centerX = iconLp.x + iconLp.width / 2;
         int centerY = iconLp.y + iconLp.height / 2;
@@ -732,6 +826,8 @@
 
     public void onItemsChanged() {
     }
+    public void onTitleChanged(CharSequence title) {
+    }
 
     public ArrayList<View> getItemsInReadingOrder() {
         return getItemsInReadingOrder(true);