summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--res/values/dimens.xml3
-rw-r--r--src/com/android/launcher3/AutoScroller.java423
-rw-r--r--src/com/android/launcher3/DragController.java7
-rw-r--r--src/com/android/launcher3/DropTarget.java2
-rw-r--r--src/com/android/launcher3/Folder.java129
-rw-r--r--src/com/android/launcher3/RampUpScroller.java120
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 be511fb9b..d5133afb6 100644
--- a/src/com/android/launcher3/Folder.java
+++ b/src/com/android/launcher3/Folder.java
@@ -109,22 +109,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;
@@ -134,12 +122,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.
*
@@ -193,15 +181,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
@@ -238,6 +217,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() {
@@ -675,83 +657,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);
}
}
@@ -796,8 +733,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;
+ }
+}