From 19072da7441e07d5e90026d9e9e1b38f11245e44 Mon Sep 17 00:00:00 2001 From: Adam Cohen Date: Tue, 31 May 2011 14:30:45 -0700 Subject: Adding visual feedback for folder creation` Change-Id: I47de931a07e3614e25f54d1ba5752a54993d6153 --- src/com/android/launcher2/FolderIcon.java | 184 ++++++++++++--------- .../launcher2/LauncherAppWidgetHostView.java | 6 +- src/com/android/launcher2/Workspace.java | 127 ++++++++++---- 3 files changed, 207 insertions(+), 110 deletions(-) (limited to 'src') diff --git a/src/com/android/launcher2/FolderIcon.java b/src/com/android/launcher2/FolderIcon.java index e97676d48..952d704be 100644 --- a/src/com/android/launcher2/FolderIcon.java +++ b/src/com/android/launcher2/FolderIcon.java @@ -78,10 +78,7 @@ public class FolderIcon extends FrameLayout implements DropTarget, FolderListene private int mOriginalWidth = -1; private int mOriginalHeight = -1; - private int mFolderLocX; - private int mFolderLocY; - private float mOuterRingScale; - private float mInnerRingScale; + FolderRingAnimator mFolderRingAnimator = null; public FolderIcon(Context context, AttributeSet attrs) { super(context, attrs); @@ -117,18 +114,107 @@ public class FolderIcon extends FrameLayout implements DropTarget, FolderListene folder.setFolderIcon(icon); folder.bind(folderInfo); icon.mFolder = folder; - + icon.mFolderRingAnimator = new FolderRingAnimator(launcher, icon); folderInfo.addListener(icon); - if (sFolderOuterRingDrawable == null) { - sFolderOuterRingDrawable = - launcher.getResources().getDrawable(R.drawable.portal_ring_outer_holo); + + return icon; + } + + public static class FolderRingAnimator { + public int mFolderLocX; + public int mFolderLocY; + public float mOuterRingScale; + public float mInnerRingScale; + public FolderIcon mFolderIcon = null; + private Launcher mLauncher; + + public FolderRingAnimator(Launcher launcher, FolderIcon folderIcon) { + mLauncher = launcher; + mFolderIcon = folderIcon; + if (sFolderOuterRingDrawable == null) { + sFolderOuterRingDrawable = + launcher.getResources().getDrawable(R.drawable.portal_ring_outer_holo); + } + if (sFolderInnerRingDrawable == null) { + sFolderInnerRingDrawable = + launcher.getResources().getDrawable(R.drawable.portal_ring_inner_holo); + } } - if (sFolderInnerRingDrawable == null) { - sFolderInnerRingDrawable = - launcher.getResources().getDrawable(R.drawable.portal_ring_inner_holo); + public void setLocation(int x, int y) { + mFolderLocX = x; + mFolderLocY = y; + } + + public void animateToAcceptState() { + ValueAnimator va = ValueAnimator.ofFloat(0f, 1f); + va.setDuration(CONSUMPTION_ANIMATION_DURATION); + va.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; + mLauncher.getWorkspace().invalidate(); + if (mFolderIcon != null) { + mFolderIcon.invalidate(); + } + } + }); + va.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + // Instead of setting the background drawable to null, we set the color to + // transparent. Setting the background drawable to null results in onDraw + // not getting called. + if (mFolderIcon != null) { + mFolderIcon.setBackgroundColor(Color.TRANSPARENT); + mFolderIcon.requestLayout(); + } + } + }); + va.start(); + } + + public void animateToNaturalState() { + ValueAnimator va = ValueAnimator.ofFloat(0f, 1f); + va.setDuration(CONSUMPTION_ANIMATION_DURATION); + va.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; + mLauncher.getWorkspace().invalidate(); + if (mFolderIcon != null) { + mFolderIcon.invalidate(); + } + } + }); + va.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + if (mFolderIcon != null) { + mFolderIcon.setBackgroundDrawable(sFolderInnerRingDrawable); + } + mLauncher.getWorkspace().hideFolderAccept(FolderRingAnimator.this); + } + }); + va.start(); + } + + public void getLocation(int[] loc) { + loc[0] = mFolderLocX; + loc[1] = mFolderLocY; + } + + public float getOuterRingScale() { + return mOuterRingScale; + } + + public float getInnerRingScale() { + return mInnerRingScale; } - return icon; } private boolean willAcceptItem(ItemInfo item) { @@ -166,69 +252,22 @@ public class FolderIcon extends FrameLayout implements DropTarget, FolderListene mOriginalHeight = lp.height; } - private void animateToAcceptState() { - ValueAnimator va = ValueAnimator.ofFloat(0f, 1f); - va.setDuration(CONSUMPTION_ANIMATION_DURATION); - va.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; - mLauncher.getWorkspace().invalidate(); - invalidate(); - } - }); - va.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - // Instead of setting the background drawable to null, we set the color to - // transparent. Setting the background drawable to null results in onDraw - // not getting called. - setBackgroundColor(Color.TRANSPARENT); - requestLayout(); - } - }); - va.start(); - } - - private void animateToNaturalState() { - ValueAnimator va = ValueAnimator.ofFloat(0f, 1f); - va.setDuration(CONSUMPTION_ANIMATION_DURATION); - va.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; - mLauncher.getWorkspace().invalidate(); - invalidate(); - } - }); - va.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - setBackgroundDrawable(sFolderInnerRingDrawable); - mLauncher.getWorkspace().hideFolderAccept(FolderIcon.this); - } - }); - va.start(); - } - private void determineFolderLocationInWorkspace() { int tvLocation[] = new int[2]; int wsLocation[] = new int[2]; getLocationOnScreen(tvLocation); mLauncher.getWorkspace().getLocationOnScreen(wsLocation); - mFolderLocX = tvLocation[0] - wsLocation[0] + getMeasuredWidth() / 2; - mFolderLocY = tvLocation[1] - wsLocation[1] + getMeasuredHeight() / 2; + + int x = tvLocation[0] - wsLocation[0] + getMeasuredWidth() / 2; + int y = tvLocation[1] - wsLocation[1] + getMeasuredHeight() / 2; + mFolderRingAnimator.setLocation(x, y); } public void onDragEnter(DragObject d) { if (!willAcceptItem((ItemInfo) d.dragInfo)) return; determineFolderLocationInWorkspace(); - mLauncher.getWorkspace().showFolderAccept(this); - animateToAcceptState(); + mLauncher.getWorkspace().showFolderAccept(mFolderRingAnimator); + mFolderRingAnimator.animateToAcceptState(); } public void onDragOver(DragObject d) { @@ -236,26 +275,13 @@ public class FolderIcon extends FrameLayout implements DropTarget, FolderListene public void onDragExit(DragObject d) { if (!willAcceptItem((ItemInfo) d.dragInfo)) return; - animateToNaturalState(); + mFolderRingAnimator.animateToNaturalState(); } public DropTarget getDropTargetDelegate(DragObject d) { return null; } - public void getFolderLocation(int[] loc) { - loc[0] = mFolderLocX; - loc[1] = mFolderLocY; - } - - public float getOuterRingScale() { - return mOuterRingScale; - } - - public float getInnerRingScale() { - return mInnerRingScale; - } - @Override protected void onDraw(Canvas canvas) { if (mFolder == null) return; diff --git a/src/com/android/launcher2/LauncherAppWidgetHostView.java b/src/com/android/launcher2/LauncherAppWidgetHostView.java index 099a676bb..670c8b666 100644 --- a/src/com/android/launcher2/LauncherAppWidgetHostView.java +++ b/src/com/android/launcher2/LauncherAppWidgetHostView.java @@ -60,7 +60,7 @@ public class LauncherAppWidgetHostView extends AppWidgetHostView mHasPerformedLongPress = false; return true; } - + // Watch for longpress events at this level to make sure // users can always pick up this widget switch (ev.getAction()) { @@ -68,7 +68,7 @@ public class LauncherAppWidgetHostView extends AppWidgetHostView postCheckForLongClick(); break; } - + case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: mHasPerformedLongPress = false; @@ -77,7 +77,7 @@ public class LauncherAppWidgetHostView extends AppWidgetHostView } break; } - + // Otherwise continue letting touch events fall through to children return false; } diff --git a/src/com/android/launcher2/Workspace.java b/src/com/android/launcher2/Workspace.java index 7d2626d1f..f3535f143 100644 --- a/src/com/android/launcher2/Workspace.java +++ b/src/com/android/launcher2/Workspace.java @@ -67,6 +67,7 @@ import android.widget.TextView; import android.widget.Toast; import com.android.launcher.R; +import com.android.launcher2.FolderIcon.FolderRingAnimator; import com.android.launcher2.InstallWidgetReceiver.WidgetMimeTypeHandlerData; /** @@ -219,7 +220,16 @@ public class Workspace extends SmoothPagedView private int mLastDragXOffset; private int mLastDragYOffset; - private ArrayList mFolderOuterRings = new ArrayList(); + private ArrayList mFolderOuterRings = new ArrayList(); + + // Variables relating to the creation of user folders by hovering shortcuts over shortcuts + private static final int FOLDER_CREATION_TIMEOUT = 400; + private final Alarm mFolderCreationAlarm = new Alarm(); + private FolderRingAnimator mDragFolderRingAnimator = null; + private View mLastDragOverView = null; + private boolean mCreateUserFolderOnDrop = false; + private int mCellWidth = -1; + private int mCellHeight = -1; // Variables relating to touch disambiguation (scrolling workspace vs. scrolling a widget) private float mXDown; @@ -896,7 +906,6 @@ public class Workspace extends SmoothPagedView } float fractionToCatchUpIn1MsVertical = mVerticalCatchupConstant; - fractionToCatchUpIn1MsHorizontal /= 33f; fractionToCatchUpIn1MsVertical /= 33f; @@ -1184,14 +1193,15 @@ public class Workspace extends SmoothPagedView } } - public void showFolderAccept(FolderIcon fi) { - mFolderOuterRings.add(fi); + public void showFolderAccept(FolderRingAnimator fra) { + mFolderOuterRings.add(fra); } - public void hideFolderAccept(FolderIcon fi) { - if (mFolderOuterRings.contains(fi)) { - mFolderOuterRings.remove(fi); + public void hideFolderAccept(FolderRingAnimator fra) { + if (mFolderOuterRings.contains(fra)) { + mFolderOuterRings.remove(fra); } + invalidate(); } @Override @@ -1234,24 +1244,28 @@ public class Workspace extends SmoothPagedView for (int i = 0; i < mFolderOuterRings.size(); i++) { // Draw outer ring - FolderIcon fi = mFolderOuterRings.get(i); + FolderRingAnimator fra = mFolderOuterRings.get(i); Drawable d = FolderIcon.sFolderOuterRingDrawable; - int width = (int) (d.getIntrinsicWidth() * fi.getOuterRingScale()); - int height = (int) (d.getIntrinsicHeight() * fi.getOuterRingScale()); - fi.getFolderLocation(mTempLocation); + int width = (int) (d.getIntrinsicWidth() * fra.getOuterRingScale()); + int height = (int) (d.getIntrinsicHeight() * fra.getOuterRingScale()); + fra.getLocation(mTempLocation); int x = mTempLocation[0] + mScrollX - width / 2; int y = mTempLocation[1] + mScrollY - height / 2; d.setBounds(x, y, x + width, y + height); d.draw(canvas); // Draw inner ring - d = FolderIcon.sFolderInnerRingDrawable; - width = (int) (fi.getMeasuredWidth() * fi.getInnerRingScale()); - height = (int) (fi.getMeasuredHeight() * fi.getInnerRingScale()); - x = mTempLocation[0] + mScrollX - width / 2; - y = mTempLocation[1] + mScrollY - height / 2; - d.setBounds(x, y, x + width, y + height); - d.draw(canvas); + if (fra.mFolderIcon != null) { + int folderWidth = fra.mFolderIcon != null ? fra.mFolderIcon.getMeasuredWidth() : mCellWidth; + int folderHeight = fra.mFolderIcon != null ? fra.mFolderIcon.getMeasuredWidth() : mCellHeight; + d = FolderIcon.sFolderInnerRingDrawable; + width = (int) (folderWidth * fra.getInnerRingScale()); + height = (int) (folderHeight * fra.getInnerRingScale()); + x = mTempLocation[0] + mScrollX - width / 2; + y = mTempLocation[1] + mScrollY - height / 2; + d.setBounds(x, y, x + width, y + height); + d.draw(canvas); + } } super.onDraw(canvas); } @@ -2404,9 +2418,7 @@ public class Workspace extends SmoothPagedView } boolean willCreateUserFolder(ItemInfo info, CellLayout target, int originX, int originY) { - mTargetCell = findNearestArea(originX, originY, - 1, 1, target, - mTargetCell); + mTargetCell = findNearestArea(originX, originY, 1, 1, target, mTargetCell); View v = target.getChildAt(mTargetCell[0], mTargetCell[1]); boolean hasntMoved = mDragInfo != null && (mDragInfo.cellX == mTargetCell[0] && @@ -2437,8 +2449,8 @@ public class Workspace extends SmoothPagedView boolean hasntMoved = mDragInfo != null && (mDragInfo.cellX == mTargetCell[0] && mDragInfo.cellY == mTargetCell[1]); - if (v == null || hasntMoved) return false; - + if (v == null || hasntMoved || !mCreateUserFolderOnDrop) return false; + mCreateUserFolderOnDrop = false; final int screen = (mTargetCell == null) ? mDragInfo.screen : indexOfChild(target); @@ -3047,24 +3059,83 @@ public class Workspace extends SmoothPagedView mapPointFromSelfToChild(mDragTargetLayout, mDragViewVisualCenter, null); ItemInfo info = (ItemInfo) d.dragInfo; - if (!willCreateUserFolder(info, mDragTargetLayout, - (int) mDragViewVisualCenter[0], (int) mDragViewVisualCenter[1])) { + boolean willCreateUserFolder = willCreateUserFolder(info, mDragTargetLayout, + (int) mDragViewVisualCenter[0], (int) mDragViewVisualCenter[1]); + + View newDropOver = null; + if (willCreateUserFolder) { + newDropOver = mDragTargetLayout.getChildAt(mTargetCell[0], mTargetCell[1]); + } + + if (newDropOver != mLastDragOverView || !willCreateUserFolder) { + if (mDragFolderRingAnimator != null && mCreateUserFolderOnDrop) { + mDragFolderRingAnimator.animateToNaturalState(); + } + mCreateUserFolderOnDrop = false; + mFolderCreationAlarm.cancelAlarm(); mIsDraggingOverIcon = false; + } + + if (willCreateUserFolder && !mIsDraggingOverIcon) { + mIsDraggingOverIcon = true; + + mLastDragOverView = mDragTargetLayout.getChildAt(mTargetCell[0], mTargetCell[1]); + mFolderCreationAlarm.setOnAlarmListener(new FolderCreationAlarmListener(mLastDragOverView)); + mFolderCreationAlarm.setAlarm(FOLDER_CREATION_TIMEOUT); + + mDragTargetLayout.clearDragOutlines(); + } + + if (!mCreateUserFolderOnDrop) { mDragTargetLayout.visualizeDropLocation(child, mDragOutline, (int) mDragViewVisualCenter[0], (int) mDragViewVisualCenter[1], item.spanX, item.spanY); - } else if (!mIsDraggingOverIcon) { - mIsDraggingOverIcon = true; - mDragTargetLayout.clearDragOutlines(); } } } } } + class FolderCreationAlarmListener implements OnAlarmListener { + View v; + + public FolderCreationAlarmListener(View v) { + this.v = v; + } + + public void onAlarm(Alarm alarm) { + int tvLocation[] = new int[2]; + int wsLocation[] = new int[2]; + v.getLocationOnScreen(tvLocation); + getLocationOnScreen(wsLocation); + + if (mCellWidth < 0 || mCellHeight < 0) { + mCellWidth = mDragTargetLayout.getCellWidth(); + mCellHeight = mDragTargetLayout.getCellHeight(); + } + int x = tvLocation[0] - wsLocation[0] + mCellWidth / 2; + int y = tvLocation[1] - wsLocation[1] + mCellHeight / 2; + + if (mDragFolderRingAnimator == null) { + mDragFolderRingAnimator = new FolderRingAnimator(mLauncher, null); + } + mDragFolderRingAnimator.setLocation(x, y); + mDragFolderRingAnimator.animateToAcceptState(); + showFolderAccept(mDragFolderRingAnimator); + mCreateUserFolderOnDrop = true; + mDragTargetLayout.clearDragOutlines(); + } + } + private void doDragExit() { mWasSpringLoadedOnDragExit = mShrinkState == ShrinkState.SPRING_LOADED; + + if (mDragFolderRingAnimator != null && mCreateUserFolderOnDrop) { + mDragFolderRingAnimator.animateToNaturalState(); + } + mFolderCreationAlarm.cancelAlarm(); + if (mDragTargetLayout != null) { mDragTargetLayout.onDragExit(); } -- cgit v1.2.3