diff options
author | Mindy DelliCarpini <mindyp@google.com> | 2013-07-03 21:31:15 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2013-07-03 21:31:15 +0000 |
commit | 70c987a0fea53583b22650bed6251e2dcc68bdac (patch) | |
tree | e7aac2d39a5411b4a72a3f02d3a89e797e3d5192 | |
parent | ee0ce2b29f9cb06cc143c6f30ffc77f4915b0da9 (diff) | |
parent | 53b8d071ce63b2b55fa8bdc71488c44308bfcf2f (diff) | |
download | android_packages_apps_Trebuchet-70c987a0fea53583b22650bed6251e2dcc68bdac.tar.gz android_packages_apps_Trebuchet-70c987a0fea53583b22650bed6251e2dcc68bdac.tar.bz2 android_packages_apps_Trebuchet-70c987a0fea53583b22650bed6251e2dcc68bdac.zip |
Merge "Use autoscroller to handle drag scrolling" into jb-ub-gel-agar
-rw-r--r-- | res/values/dimens.xml | 3 | ||||
-rw-r--r-- | src/com/android/launcher3/AutoScroller.java | 423 | ||||
-rw-r--r-- | src/com/android/launcher3/DragController.java | 7 | ||||
-rw-r--r-- | src/com/android/launcher3/DropTarget.java | 2 | ||||
-rw-r--r-- | src/com/android/launcher3/Folder.java | 129 | ||||
-rw-r--r-- | src/com/android/launcher3/RampUpScroller.java | 120 |
6 files changed, 581 insertions, 103 deletions
diff --git a/res/values/dimens.xml b/res/values/dimens.xml index 231b77673..3fe53d274 100644 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -102,9 +102,6 @@ <!-- When dragging an item, how much bigger (fixed dps) the dragged view should be. If 0, it will not be scaled at all. --> <dimen name="dragViewScale">12dp</dimen> - <dimen name="min_scroll_band_size">48dp</dimen> - <integer name="scroll_band_factor">20</integer> - <integer name="scroll_band_max_factor">30</integer> <!-- Padding applied to AppWidget previews --> <dimen name="app_widget_preview_padding_left">16dp</dimen> diff --git a/src/com/android/launcher3/AutoScroller.java b/src/com/android/launcher3/AutoScroller.java new file mode 100644 index 000000000..ac8e2e61a --- /dev/null +++ b/src/com/android/launcher3/AutoScroller.java @@ -0,0 +1,423 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.launcher3; + +import android.graphics.Rect; +import android.graphics.RectF; +import android.os.SystemClock; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewConfiguration; +import android.view.animation.AccelerateInterpolator; +import android.view.animation.Interpolator; +import android.widget.AbsListView; + +class AutoScroller implements View.OnTouchListener, Runnable { + private static final int SCALE_RELATIVE = 0; + private static final int SCALE_ABSOLUTE = 1; + + private final View mTarget; + private final RampUpScroller mScroller; + + /** Interpolator used to scale velocity with touch position, may be null. */ + private Interpolator mEdgeInterpolator = new AccelerateInterpolator(); + + /** + * Type of maximum velocity scaling to use, one of: + * <ul> + * <li>{@link #SCALE_RELATIVE} + * <li>{@link #SCALE_ABSOLUTE} + * </ul> + */ + private int mMaxVelocityScale = SCALE_RELATIVE; + + /** + * Type of activation edge scaling to use, one of: + * <ul> + * <li>{@link #SCALE_RELATIVE} + * <li>{@link #SCALE_ABSOLUTE} + * </ul> + */ + private int mActivationEdgeScale = SCALE_RELATIVE; + + /** Edge insets used to activate auto-scrolling. */ + private RectF mActivationEdges = new RectF(0.2f, 0.2f, 0.2f, 0.2f); + + /** Delay after entering an activation edge before auto-scrolling begins. */ + private int mActivationDelay; + + /** Maximum horizontal scrolling velocity. */ + private float mMaxVelocityX = 0.001f; + + /** Maximum vertical scrolling velocity. */ + private float mMaxVelocityY = 0.001f; + + /** + * Whether positive insets should also extend beyond the view bounds when + * auto-scrolling is already active. This allows a user to start scrolling + * at an inside edge, then move beyond the edge and continue scrolling. + */ + private boolean mExtendsBeyondEdges = true; + + /** Whether to start activation immediately. */ + private boolean mSkipDelay; + + /** Whether to reset the scroller start time on the next animation. */ + private boolean mResetScroller; + + /** Whether the auto-scroller is active. */ + private boolean mActive; + private long[] mScrollStart = new long[2]; + + /** + * If the event is within this percentage of the edge of the scrolling area, + * use accelerated scrolling. + */ + private float mFastScrollingRange = 0.8f; + + /** + * Duration of time spent in accelerated scrolling area before reaching + * maximum velocity + */ + private float mDurationToMax = 2500f; + + private static final int X = 0; + private static final int Y = 1; + + public AutoScroller(View target) { + mTarget = target; + mScroller = new RampUpScroller(250); + mActivationDelay = ViewConfiguration.getTapTimeout(); + } + + /** + * Sets the maximum scrolling velocity as a fraction of the host view size + * per second. For example, a maximum Y velocity of 1 would scroll one + * vertical page per second. By default, both values are 1. + * + * @param x The maximum X velocity as a fraction of the host view width per + * second. + * @param y The maximum Y velocity as a fraction of the host view height per + * second. + */ + public void setMaximumVelocityRelative(float x, float y) { + mMaxVelocityScale = SCALE_RELATIVE; + mMaxVelocityX = x / 1000f; + mMaxVelocityY = y / 1000f; + } + + /** + * Sets the maximum scrolling velocity as an absolute pixel distance per + * second. For example, a maximum Y velocity of 100 would scroll one hundred + * pixels per second. + * + * @param x The maximum X velocity as a fraction of the host view width per + * second. + * @param y The maximum Y velocity as a fraction of the host view height per + * second. + */ + public void setMaximumVelocityAbsolute(float x, float y) { + mMaxVelocityScale = SCALE_ABSOLUTE; + mMaxVelocityX = x / 1000f; + mMaxVelocityY = y / 1000f; + } + + /** + * Sets the delay after entering an activation edge before activation of + * auto-scrolling. By default, the activation delay is set to + * {@link ViewConfiguration#getTapTimeout()}. + * + * @param delayMillis The delay in milliseconds. + */ + public void setActivationDelay(int delayMillis) { + mActivationDelay = delayMillis; + } + + /** + * Sets the activation edges in pixels. Edges are treated as insets, so + * positive values expand into the view bounds while negative values extend + * outside the bounds. + * + * @param l The left activation edge, in pixels. + * @param t The top activation edge, in pixels. + * @param r The right activation edge, in pixels. + * @param b The bottom activation edge, in pixels. + */ + public void setEdgesAbsolute(int l, int t, int r, int b) { + mActivationEdgeScale = SCALE_ABSOLUTE; + mActivationEdges.set(l, t, r, b); + } + + /** + * Whether positive insets should also extend beyond the view bounds when + * auto-scrolling is already active. This allows a user to start scrolling + * at an inside edge, then move beyond the edge and continue scrolling. + * + * @param e + */ + public void setExtendsBeyondEdges(boolean e) { + mExtendsBeyondEdges = e; + } + + /** + * Sets the activation edges as fractions of the host view size. Edges are + * treated as insets, so positive values expand into the view bounds while + * negative values extend outside the bounds. By default, all values are + * 0.25. + * + * @param l The left activation edge, as a fraction of view size. + * @param t The top activation edge, as a fraction of view size. + * @param r The right activation edge, as a fraction of view size. + * @param b The bottom activation edge, as a fraction of view size. + */ + public void setEdgesRelative(float l, float t, float r, float b) { + mActivationEdgeScale = SCALE_RELATIVE; + mActivationEdges.set(l, t, r, b); + } + + /** + * Sets the {@link Interpolator} used for scaling touches within activation + * edges. By default, uses the {@link AccelerateInterpolator} to gradually + * speed up scrolling. + * + * @param edgeInterpolator The interpolator to use for activation edges, or + * {@code null} to use a fixed velocity during auto-scrolling. + */ + public void setEdgeInterpolator(Interpolator edgeInterpolator) { + mEdgeInterpolator = edgeInterpolator; + } + + /** + * Stop tracking scrolling. + */ + public void stop() { + stop(true); + } + + /** + * Pass the rectangle defining the drawing region for the object used to + * trigger drag scrolling. + * + * @param v View on which the scrolling regions are defined + * @param r Rect defining the drawing bounds of the object being dragged + * @return whether the event was handled + */ + public boolean onTouch(View v, Rect r) { + MotionEvent event = MotionEvent.obtain(SystemClock.uptimeMillis(), + SystemClock.uptimeMillis(), MotionEvent.ACTION_MOVE, r.left, r.top, 0); + return onTouch(v, event); + } + + @Override + public boolean onTouch(View v, MotionEvent event) { + final int action = event.getActionMasked(); + switch (action) { + case MotionEvent.ACTION_DOWN: + case MotionEvent.ACTION_MOVE: + final int sourceWidth = v.getWidth(); + final int sourceHeight = v.getHeight(); + final float x = event.getX(); + final float y = event.getY(); + final float l; + final float t; + final float r; + final float b; + final RectF activationEdges = mActivationEdges; + if (mActivationEdgeScale == SCALE_ABSOLUTE) { + l = activationEdges.left; + t = activationEdges.top; + r = activationEdges.right; + b = activationEdges.bottom; + } else { + l = activationEdges.left * sourceWidth; + t = activationEdges.top * sourceHeight; + r = activationEdges.right * sourceWidth; + b = activationEdges.bottom * sourceHeight; + } + + final float maxVelX; + final float maxVelY; + if (mMaxVelocityScale == SCALE_ABSOLUTE) { + maxVelX = mMaxVelocityX; + maxVelY = mMaxVelocityY; + } else { + maxVelX = mMaxVelocityX * mTarget.getWidth(); + maxVelY = mMaxVelocityY * mTarget.getHeight(); + } + + final float velocityX = getEdgeVelocity(X, l, r, x, sourceWidth, event); + final float velocityY = getEdgeVelocity(Y, t, b, y, sourceHeight, event); + mScroller.setTargetVelocity(velocityX * maxVelX, velocityY * maxVelY); + + if ((velocityX != 0 || velocityY != 0) && !mActive) { + mActive = true; + mResetScroller = true; + if (mSkipDelay) { + mTarget.postOnAnimation(this); + } else { + mSkipDelay = true; + mTarget.postOnAnimationDelayed(this, mActivationDelay); + } + } + break; + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: + stop(true); + break; + } + + return false; + } + + /** + * @param leading Size of the leading activation inset. + * @param trailing Size of the trailing activation inset. + * @param current Position within within the total area. + * @param size Size of the total area. + * @return The fraction of the activation area. + */ + private float getEdgeVelocity(int dir, float leading, float trailing, + float current, float size, MotionEvent ev) { + float valueLeading = 0; + if (leading > 0) { + if (current < leading) { + if (current > 0) { + // Movement up to the edge is scaled. + valueLeading = 1f - current / leading; + } else if (mActive && mExtendsBeyondEdges) { + // Movement beyond the edge is always maximum. + valueLeading = 1f; + } + } + } else if (leading < 0) { + if (current < 0) { + // Movement beyond the edge is scaled. + valueLeading = current / leading; + } + } + + float valueTrailing = 0; + if (trailing > 0) { + if (current > size - trailing) { + if (current < size) { + // Movement up to the edge is scaled. + valueTrailing = 1f - (size - current) / trailing; + } else if (mActive && mExtendsBeyondEdges) { + // Movement beyond the edge is always maximum. + valueTrailing = 1f; + } + } + } else if (trailing < 0) { + if (current > size) { + // Movement beyond the edge is scaled. + valueTrailing = (size - current) / trailing; + } + } + + float value = (valueTrailing - valueLeading); + if ((value > mFastScrollingRange || value < -mFastScrollingRange) + && mScrollStart[dir] == 0) { + // within auto scrolling area + mScrollStart[dir] = ev.getEventTime(); + } else { + // Outside fast scrolling area; reset duration + mScrollStart[dir] = 0; + } + final float duration = (ev.getEventTime() - mScrollStart[dir])/mDurationToMax; + final float interpolated; + if (value < 0) { + if (value < -mFastScrollingRange) { + // Close to top; use duration! + value += mEdgeInterpolator.getInterpolation(-duration); + } + interpolated = mEdgeInterpolator == null ? -1 + : -mEdgeInterpolator.getInterpolation(-value); + } else if (value > 0) { + // Close to bottom; use duration + if (value > mFastScrollingRange) { + // Close to bottom; use duration! + value += mEdgeInterpolator.getInterpolation(duration); + } + interpolated = mEdgeInterpolator == null ? 1 + : mEdgeInterpolator.getInterpolation(value); + } else { + mScrollStart[dir] = 0; + return 0; + } + + return constrain(interpolated, -1, 1); + } + + private static float constrain(float value, float min, float max) { + if (value > max) { + return max; + } else if (value < min) { + return min; + } else { + return value; + } + } + + /** + * Stops auto-scrolling immediately, optionally reseting the auto-scrolling + * delay. + * + * @param reset Whether to reset the auto-scrolling delay. + */ + private void stop(boolean reset) { + mActive = false; + mSkipDelay = !reset; + mTarget.removeCallbacks(this); + } + + @Override + public void run() { + if (!mActive) { + return; + } + + if (mResetScroller) { + mResetScroller = false; + mScroller.start(); + } + + final View target = mTarget; + final RampUpScroller scroller = mScroller; + final float targetVelocityX = scroller.getTargetVelocityX(); + final float targetVelocityY = scroller.getTargetVelocityY(); + if ((targetVelocityY == 0 || !target.canScrollVertically(targetVelocityY > 0 ? 1 : -1) + && (targetVelocityX == 0 + || !target.canScrollHorizontally(targetVelocityX > 0 ? 1 : -1)))) { + stop(false); + return; + } + + scroller.computeScrollDelta(); + + final int deltaX = scroller.getDeltaX(); + final int deltaY = scroller.getDeltaY(); + + if (target instanceof AbsListView) { + final AbsListView list = (AbsListView) target; + list.smoothScrollBy(deltaY, 0); + } else { + target.scrollBy(deltaX, deltaY); + } + + target.postOnAnimation(this); + } +} diff --git a/src/com/android/launcher3/DragController.java b/src/com/android/launcher3/DragController.java index 54f7dbc15..970ce2611 100644 --- a/src/com/android/launcher3/DragController.java +++ b/src/com/android/launcher3/DragController.java @@ -123,17 +123,16 @@ public class DragController { * Interface to receive notifications when a drag starts or stops */ interface DragListener { - /** * A drag has begun - * + * * @param source An object representing where the drag originated * @param info The data associated with the object that is being dragged * @param dragAction The drag action: either {@link DragController#DRAG_ACTION_MOVE} * or {@link DragController#DRAG_ACTION_COPY} */ void onDragStart(DragSource source, Object info, int dragAction); - + /** * The drag has ended */ @@ -514,7 +513,6 @@ public class DragController { if (delegate != null) { dropTarget = delegate; } - if (mLastDropTarget != dropTarget) { if (mLastDropTarget != null) { mLastDropTarget.onDragExit(mDragObject); @@ -589,6 +587,7 @@ public class DragController { } else { mScrollState = SCROLL_OUTSIDE_ZONE; } + handleMoveEvent(dragLayerX, dragLayerY); break; case MotionEvent.ACTION_MOVE: handleMoveEvent(dragLayerX, dragLayerY); diff --git a/src/com/android/launcher3/DropTarget.java b/src/com/android/launcher3/DropTarget.java index 3ecb8ff08..fa364fadf 100644 --- a/src/com/android/launcher3/DropTarget.java +++ b/src/com/android/launcher3/DropTarget.java @@ -123,7 +123,7 @@ public interface DropTarget { * touch happened * @param dragView The DragView that's being dragged around on screen. * @param dragInfo Data associated with the object being dragged - * + * */ void onDrop(DragObject dragObject); diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java index 36b6bf41f..cf1a43225 100644 --- a/src/com/android/launcher3/Folder.java +++ b/src/com/android/launcher3/Folder.java @@ -107,22 +107,10 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList FolderEditText mFolderName; private float mFolderIconPivotX; private float mFolderIconPivotY; - private long mDragScrollStart; - - /** Interpolator used to scale velocity with touch position, may be null. */ - private Interpolator mEdgeInterpolator = new AccelerateInterpolator(); private static final int SCROLL_CUT_OFF_AMOUNT = 60; - private final float mDurationToMax = 300f; - private final float mMaxVelocity = 100000f/1000f; - - // Aim for this amount of target area to trigger scrolling - private static float sScrollBandFactor = -1f; - private static float sScrollBandMaxFactor = -1f; - - // Min size for the target area to trigger scrolling - private static int sMinScrollBandHeight = -1; + private static final float MAX_SCROLL_VELOCITY = 1500f; private boolean mIsEditingName = false; private InputMethodManager mInputMethodManager; @@ -132,12 +120,12 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList private int DRAG_MODE_NONE = 0; private int DRAG_MODE_REORDER = 1; - private int DRAG_MODE_SCROLL_UP = 2; - private int DRAG_MODE_SCROLL_DOWN = 3; private int mDragMode = DRAG_MODE_NONE; private boolean mDestroyed; + private AutoScroller mAutoScroller; + /** * Used to inflate the Workspace from XML. * @@ -191,15 +179,6 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList if (sHintText == null) { sHintText = res.getString(R.string.folder_hint_text); } - if (sMinScrollBandHeight == -1) { - sMinScrollBandHeight = res.getDimensionPixelSize(R.dimen.min_scroll_band_size); - } - if (sScrollBandFactor == -1f) { - sScrollBandFactor = res.getInteger(R.integer.scroll_band_factor)/100f; - } - if (sScrollBandMaxFactor == -1f) { - sScrollBandMaxFactor = res.getInteger(R.integer.scroll_band_max_factor)/100f; - } mLauncher = (Launcher) context; // We need this view to be focusable in touch mode so that when text editing of the folder // name is complete, we have something to focus on, thus hiding the cursor and giving @@ -236,6 +215,9 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList mFolderName.setSelectAllOnFocus(true); mFolderName.setInputType(mFolderName.getInputType() | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS | InputType.TYPE_TEXT_FLAG_CAP_WORDS); + mAutoScroller = new AutoScroller(mScrollView); + mAutoScroller.setMaximumVelocityAbsolute(MAX_SCROLL_VELOCITY, MAX_SCROLL_VELOCITY); + mAutoScroller.setExtendsBeyondEdges(false); } private ActionMode.Callback mActionModeCallback = new ActionMode.Callback() { @@ -673,83 +655,38 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList return (getLayoutDirection() == LAYOUT_DIRECTION_RTL); } + private Rect getDragObjectDrawingRect(View dragView, float[] r) { + final Rect drawingRect = mTempRect; + drawingRect.left = (int) r[0]; + drawingRect.top = (int) r[1]; + drawingRect.right = drawingRect.left + dragView.getMeasuredWidth(); + drawingRect.bottom = drawingRect.top + dragView.getMeasuredHeight(); + return drawingRect; + } + public void onDragOver(DragObject d) { int scrollOffset = mScrollView.getScrollY(); - int height = getMeasuredHeight(); - int scrollBandHeight = Math.min((int) (height * sScrollBandMaxFactor), - (Math.max(sMinScrollBandHeight, (int) (height * sScrollBandFactor)))); float[] r = getDragViewVisualCenter(d.x, d.y, d.xOffset, d.yOffset, d.dragView, null); r[0] -= getPaddingLeft(); r[1] -= getPaddingTop(); + if (!mAutoScroller.onTouch(this, getDragObjectDrawingRect(d.dragView, r))) { + mTargetCell = mContent.findNearestArea((int) r[0], (int) r[1] + scrollOffset, 1, 1, + mTargetCell); - mTargetCell = mContent.findNearestArea((int) r[0], (int) r[1] + scrollOffset, - 1, 1, mTargetCell); - - if (isLayoutRtl()) { - mTargetCell[0] = mContent.getCountX() - mTargetCell[0] - 1; - } - - if (r[1] < scrollBandHeight && mScrollView.getScrollY() > 0) { - // Scroll up - if (mDragMode != DRAG_MODE_SCROLL_UP) { - mDragMode = DRAG_MODE_SCROLL_UP; - mDragScrollStart = System.currentTimeMillis(); - scrollUp(); + if (isLayoutRtl()) { + mTargetCell[0] = mContent.getCountX() - mTargetCell[0] - 1; } - mReorderAlarm.cancelAlarm(); - } else if (r[1] > (getFolderHeight() - scrollBandHeight) && mScrollView.getScrollY() < - (mContent.getMeasuredHeight() - mScrollView.getMeasuredHeight())) { - if (mDragMode != DRAG_MODE_SCROLL_DOWN) { - mDragMode = DRAG_MODE_SCROLL_DOWN; - mDragScrollStart = System.currentTimeMillis(); - scrollDown(); + if (mTargetCell[0] != mPreviousTargetCell[0] + || mTargetCell[1] != mPreviousTargetCell[1]) { + mReorderAlarm.cancelAlarm(); + mReorderAlarm.setOnAlarmListener(mReorderAlarmListener); + mReorderAlarm.setAlarm(REORDER_DELAY); + mPreviousTargetCell[0] = mTargetCell[0]; + mPreviousTargetCell[1] = mTargetCell[1]; + mDragMode = DRAG_MODE_REORDER; + } else { + mDragMode = DRAG_MODE_NONE; } - mReorderAlarm.cancelAlarm(); - } else if (mTargetCell[0] != mPreviousTargetCell[0] || mTargetCell[1] != mPreviousTargetCell[1]) { - mReorderAlarm.cancelAlarm(); - mReorderAlarm.setOnAlarmListener(mReorderAlarmListener); - mReorderAlarm.setAlarm(REORDER_DELAY); - mPreviousTargetCell[0] = mTargetCell[0]; - mPreviousTargetCell[1] = mTargetCell[1]; - mDragMode = DRAG_MODE_REORDER; - } else { - mDragMode = DRAG_MODE_NONE; - } - } - - Runnable mScrollUpRunnable = new Runnable() { - @Override - public void run() { - scrollUp(); - } - }; - - Runnable mScrollDownRunnable = new Runnable() { - @Override - public void run() { - scrollDown(); - } - }; - - private int getVelocity() { - float duration = (System.currentTimeMillis() - mDragScrollStart)/mDurationToMax; - return (int) Math.min(mMaxVelocity, - (int) (mEdgeInterpolator.getInterpolation(duration) * mMaxVelocity)); - } - - private void scrollUp() { - if (mDragMode == DRAG_MODE_SCROLL_UP) { - mScrollView.setScrollY(mScrollView.getScrollY() - getVelocity()); - invalidate(); - post(mScrollUpRunnable); - } - } - - private void scrollDown() { - if (mDragMode == DRAG_MODE_SCROLL_DOWN) { - mScrollView.setScrollY(mScrollView.getScrollY() + getVelocity()); - invalidate(); - post(mScrollDownRunnable); } } @@ -794,8 +731,10 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList } public void onDragExit(DragObject d) { - // We only close the folder if this is a true drag exit, ie. not because a drop - // has occurred above the folder. + // Exiting folder; stop the auto scroller. + mAutoScroller.stop(); + // We only close the folder if this is a true drag exit, ie. not because + // a drop has occurred above the folder. if (!d.dragComplete) { mOnExitAlarm.setOnAlarmListener(mOnExitAlarmListener); mOnExitAlarm.setAlarm(ON_EXIT_CLOSE_DELAY); diff --git a/src/com/android/launcher3/RampUpScroller.java b/src/com/android/launcher3/RampUpScroller.java new file mode 100644 index 000000000..89eb5798e --- /dev/null +++ b/src/com/android/launcher3/RampUpScroller.java @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.launcher3; + +import android.view.animation.AccelerateInterpolator; +import android.view.animation.AnimationUtils; +import android.view.animation.Interpolator; + +/** + * Scroller that gradually reaches a target velocity. + */ +class RampUpScroller { + private final Interpolator mInterpolator; + private final long mRampUpTime; + + private long mStartTime; + private long mDeltaTime; + private float mTargetVelocityX; + private float mTargetVelocityY; + private int mDeltaX; + private int mDeltaY; + + /** + * Creates a new ramp-up scroller that reaches full velocity after a + * specified duration. + * + * @param rampUpTime Duration before the scroller reaches target velocity. + */ + public RampUpScroller(long rampUpTime) { + mInterpolator = new AccelerateInterpolator(); + mRampUpTime = rampUpTime; + } + + /** + * Starts the scroller at the current animation time. + */ + public void start() { + mStartTime = AnimationUtils.currentAnimationTimeMillis(); + mDeltaTime = mStartTime; + } + + /** + * Computes the current scroll deltas. This usually only be called after + * starting the scroller with {@link #start()}. + * + * @see #getDeltaX() + * @see #getDeltaY() + */ + public void computeScrollDelta() { + final long currentTime = AnimationUtils.currentAnimationTimeMillis(); + final long elapsedSinceStart = currentTime - mStartTime; + final float scale; + if (elapsedSinceStart < mRampUpTime) { + scale = mInterpolator.getInterpolation((float) elapsedSinceStart / mRampUpTime); + } else { + scale = 1f; + } + + final long elapsedSinceDelta = currentTime - mDeltaTime; + mDeltaTime = currentTime; + + mDeltaX = (int) (elapsedSinceDelta * scale * mTargetVelocityX); + mDeltaY = (int) (elapsedSinceDelta * scale * mTargetVelocityY); + } + + /** + * Sets the target velocity for this scroller. + * + * @param x The target X velocity in pixels per millisecond. + * @param y The target Y velocity in pixels per millisecond. + */ + public void setTargetVelocity(float x, float y) { + mTargetVelocityX = x; + mTargetVelocityY = y; + } + + /** + * @return The target X velocity for this scroller. + */ + public float getTargetVelocityX() { + return mTargetVelocityX; + } + + /** + * @return The target Y velocity for this scroller. + */ + public float getTargetVelocityY() { + return mTargetVelocityY; + } + + /** + * The distance traveled in the X-coordinate computed by the last call to + * {@link #computeScrollDelta()}. + */ + public int getDeltaX() { + return mDeltaX; + } + + /** + * The distance traveled in the Y-coordinate computed by the last call to + * {@link #computeScrollDelta()}. + */ + public int getDeltaY() { + return mDeltaY; + } +} |