summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorAlan Viverette <alanv@google.com>2013-08-28 17:53:41 -0700
committerMindy Pereira <mindyp@google.com>2013-09-03 08:32:03 -0700
commit4cda5b770db7f375a4766fe6c0d657b9d5303374 (patch)
treee6cb8760a0e81835aa1fbc69a0195f1f97e7eaf8 /src
parent8b3cd79082043a08c319f1550679d4078b7d509c (diff)
downloadandroid_packages_apps_Trebuchet-4cda5b770db7f375a4766fe6c0d657b9d5303374.tar.gz
android_packages_apps_Trebuchet-4cda5b770db7f375a4766fe6c0d657b9d5303374.tar.bz2
android_packages_apps_Trebuchet-4cda5b770db7f375a4766fe6c0d657b9d5303374.zip
Port AutoScroller to launcher3
Change-Id: Ic2b7b5faac4878f3b4645c291e36c5f7734c7f8b
Diffstat (limited to 'src')
-rw-r--r--src/com/android/launcher3/AutoScrollHelper.java867
-rw-r--r--src/com/android/launcher3/AutoScroller.java423
-rw-r--r--src/com/android/launcher3/Folder.java46
-rw-r--r--src/com/android/launcher3/FolderAutoScrollHelper.java56
4 files changed, 946 insertions, 446 deletions
diff --git a/src/com/android/launcher3/AutoScrollHelper.java b/src/com/android/launcher3/AutoScrollHelper.java
new file mode 100644
index 000000000..9a7c3b0f0
--- /dev/null
+++ b/src/com/android/launcher3/AutoScrollHelper.java
@@ -0,0 +1,867 @@
+/*
+ * 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.content.res.Resources;
+import android.os.SystemClock;
+import android.support.v4.view.MotionEventCompat;
+import android.support.v4.view.ViewCompat;
+import android.util.DisplayMetrics;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.animation.AccelerateInterpolator;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
+
+/**
+ * AutoScrollHelper is a utility class for adding automatic edge-triggered
+ * scrolling to Views.
+ * <p>
+ * <b>Note:</b> Implementing classes are responsible for overriding the
+ * {@link #scrollTargetBy}, {@link #canTargetScrollHorizontally}, and
+ * {@link #canTargetScrollVertically} methods. See
+ * {@link ListViewAutoScrollHelper} for a {@link android.widget.ListView}
+ * -specific implementation.
+ * <p>
+ * <h1>Activation</h1> Automatic scrolling starts when the user touches within
+ * an activation area. By default, activation areas are defined as the top,
+ * left, right, and bottom 20% of the host view's total area. Touching within
+ * the top activation area scrolls up, left scrolls to the left, and so on.
+ * <p>
+ * As the user touches closer to the extreme edge of the activation area,
+ * scrolling accelerates up to a maximum velocity. When using the default edge
+ * type, {@link #EDGE_TYPE_INSIDE_EXTEND}, moving outside of the view bounds
+ * will scroll at the maximum velocity.
+ * <p>
+ * The following activation properties may be configured:
+ * <ul>
+ * <li>Delay after entering activation area before auto-scrolling begins, see
+ * {@link #setActivationDelay}. Default value is
+ * {@link ViewConfiguration#getTapTimeout()} to avoid conflicting with taps.
+ * <li>Location of activation areas, see {@link #setEdgeType}. Default value is
+ * {@link #EDGE_TYPE_INSIDE_EXTEND}.
+ * <li>Size of activation areas relative to view size, see
+ * {@link #setRelativeEdges}. Default value is 20% for both vertical and
+ * horizontal edges.
+ * <li>Maximum size used to constrain relative size, see
+ * {@link #setMaximumEdges}. Default value is {@link #NO_MAX}.
+ * </ul>
+ * <h1>Scrolling</h1> When automatic scrolling is active, the helper will
+ * repeatedly call {@link #scrollTargetBy} to apply new scrolling offsets.
+ * <p>
+ * The following scrolling properties may be configured:
+ * <ul>
+ * <li>Acceleration ramp-up duration, see {@link #setRampUpDuration}. Default
+ * value is 2500 milliseconds.
+ * <li>Acceleration ramp-down duration, see {@link #setRampDownDuration}.
+ * Default value is 500 milliseconds.
+ * <li>Target velocity relative to view size, see {@link #setRelativeVelocity}.
+ * Default value is 100% per second for both vertical and horizontal.
+ * <li>Minimum velocity used to constrain relative velocity, see
+ * {@link #setMinimumVelocity}. When set, scrolling will accelerate to the
+ * larger of either this value or the relative target value. Default value is
+ * approximately 5 centimeters or 315 dips per second.
+ * <li>Maximum velocity used to constrain relative velocity, see
+ * {@link #setMaximumVelocity}. Default value is approximately 25 centimeters or
+ * 1575 dips per second.
+ * </ul>
+ */
+public abstract class AutoScrollHelper implements View.OnTouchListener {
+ /**
+ * Constant passed to {@link #setRelativeEdges} or
+ * {@link #setRelativeVelocity}. Using this value ensures that the computed
+ * relative value is ignored and the absolute maximum value is always used.
+ */
+ public static final float RELATIVE_UNSPECIFIED = 0;
+
+ /**
+ * Constant passed to {@link #setMaximumEdges}, {@link #setMaximumVelocity},
+ * or {@link #setMinimumVelocity}. Using this value ensures that the
+ * computed relative value is always used without constraining to a
+ * particular minimum or maximum value.
+ */
+ public static final float NO_MAX = Float.MAX_VALUE;
+
+ /**
+ * Constant passed to {@link #setMaximumEdges}, or
+ * {@link #setMaximumVelocity}, or {@link #setMinimumVelocity}. Using this
+ * value ensures that the computed relative value is always used without
+ * constraining to a particular minimum or maximum value.
+ */
+ public static final float NO_MIN = 0;
+
+ /**
+ * Edge type that specifies an activation area starting at the view bounds
+ * and extending inward. Moving outside the view bounds will stop scrolling.
+ *
+ * @see #setEdgeType
+ */
+ public static final int EDGE_TYPE_INSIDE = 0;
+
+ /**
+ * Edge type that specifies an activation area starting at the view bounds
+ * and extending inward. After activation begins, moving outside the view
+ * bounds will continue scrolling.
+ *
+ * @see #setEdgeType
+ */
+ public static final int EDGE_TYPE_INSIDE_EXTEND = 1;
+
+ /**
+ * Edge type that specifies an activation area starting at the view bounds
+ * and extending outward. Moving inside the view bounds will stop scrolling.
+ *
+ * @see #setEdgeType
+ */
+ public static final int EDGE_TYPE_OUTSIDE = 2;
+
+ private static final int HORIZONTAL = 0;
+ private static final int VERTICAL = 1;
+
+ /** Scroller used to control acceleration toward maximum velocity. */
+ private final ClampedScroller mScroller = new ClampedScroller();
+
+ /** Interpolator used to scale velocity with touch position. */
+ private final Interpolator mEdgeInterpolator = new AccelerateInterpolator();
+
+ /** The view to auto-scroll. Might not be the source of touch events. */
+ private final View mTarget;
+
+ /** Runnable used to animate scrolling. */
+ private Runnable mRunnable;
+
+ /** Edge insets used to activate auto-scrolling. */
+ private float[] mRelativeEdges = new float[] { RELATIVE_UNSPECIFIED, RELATIVE_UNSPECIFIED };
+
+ /** Clamping values for edge insets used to activate auto-scrolling. */
+ private float[] mMaximumEdges = new float[] { NO_MAX, NO_MAX };
+
+ /** The type of edge being used. */
+ private int mEdgeType;
+
+ /** Delay after entering an activation edge before auto-scrolling begins. */
+ private int mActivationDelay;
+
+ /** Relative scrolling velocity at maximum edge distance. */
+ private float[] mRelativeVelocity = new float[] { RELATIVE_UNSPECIFIED, RELATIVE_UNSPECIFIED };
+
+ /** Clamping values used for scrolling velocity. */
+ private float[] mMinimumVelocity = new float[] { NO_MIN, NO_MIN };
+
+ /** Clamping values used for scrolling velocity. */
+ private float[] mMaximumVelocity = new float[] { NO_MAX, NO_MAX };
+
+ /** Whether to start activation immediately. */
+ private boolean mAlreadyDelayed;
+
+ /** Whether to reset the scroller start time on the next animation. */
+ private boolean mNeedsReset;
+
+ /** Whether to send a cancel motion event to the target view. */
+ private boolean mNeedsCancel;
+
+ /** Whether the auto-scroller is actively scrolling. */
+ private boolean mAnimating;
+
+ /** Whether the auto-scroller is enabled. */
+ private boolean mEnabled;
+
+ /** Whether the auto-scroller consumes events when scrolling. */
+ private boolean mExclusive;
+
+ // Default values.
+ private static final int DEFAULT_EDGE_TYPE = EDGE_TYPE_INSIDE_EXTEND;
+ private static final int DEFAULT_MINIMUM_VELOCITY_DIPS = 315;
+ private static final int DEFAULT_MAXIMUM_VELOCITY_DIPS = 1575;
+ private static final float DEFAULT_MAXIMUM_EDGE = NO_MAX;
+ private static final float DEFAULT_RELATIVE_EDGE = 0.2f;
+ private static final float DEFAULT_RELATIVE_VELOCITY = 1f;
+ private static final int DEFAULT_ACTIVATION_DELAY = ViewConfiguration.getTapTimeout();
+ private static final int DEFAULT_RAMP_UP_DURATION = 2500;
+ private static final int DEFAULT_RAMP_DOWN_DURATION = 500;
+
+ /**
+ * Creates a new helper for scrolling the specified target view.
+ * <p>
+ * The resulting helper may be configured by chaining setter calls and
+ * should be set as a touch listener on the target view.
+ * <p>
+ * By default, the helper is disabled and will not respond to touch events
+ * until it is enabled using {@link #setEnabled}.
+ *
+ * @param target The view to automatically scroll.
+ */
+ public AutoScrollHelper(View target) {
+ mTarget = target;
+
+ final DisplayMetrics metrics = Resources.getSystem().getDisplayMetrics();
+ final int maxVelocity = (int) (DEFAULT_MAXIMUM_VELOCITY_DIPS * metrics.density + 0.5f);
+ final int minVelocity = (int) (DEFAULT_MINIMUM_VELOCITY_DIPS * metrics.density + 0.5f);
+ setMaximumVelocity(maxVelocity, maxVelocity);
+ setMinimumVelocity(minVelocity, minVelocity);
+
+ setEdgeType(DEFAULT_EDGE_TYPE);
+ setMaximumEdges(DEFAULT_MAXIMUM_EDGE, DEFAULT_MAXIMUM_EDGE);
+ setRelativeEdges(DEFAULT_RELATIVE_EDGE, DEFAULT_RELATIVE_EDGE);
+ setRelativeVelocity(DEFAULT_RELATIVE_VELOCITY, DEFAULT_RELATIVE_VELOCITY);
+ setActivationDelay(DEFAULT_ACTIVATION_DELAY);
+ setRampUpDuration(DEFAULT_RAMP_UP_DURATION);
+ setRampDownDuration(DEFAULT_RAMP_DOWN_DURATION);
+ }
+
+ /**
+ * Sets whether the scroll helper is enabled and should respond to touch
+ * events.
+ *
+ * @param enabled Whether the scroll helper is enabled.
+ * @return The scroll helper, which may used to chain setter calls.
+ */
+ public AutoScrollHelper setEnabled(boolean enabled) {
+ if (mEnabled && !enabled) {
+ requestStop();
+ }
+
+ mEnabled = enabled;
+ return this;
+ }
+
+ /**
+ * @return True if this helper is enabled and responding to touch events.
+ */
+ public boolean isEnabled() {
+ return mEnabled;
+ }
+
+ /**
+ * Enables or disables exclusive handling of touch events during scrolling.
+ * By default, exclusive handling is disabled and the target view receives
+ * all touch events.
+ * <p>
+ * When enabled, {@link #onTouch} will return true if the helper is
+ * currently scrolling and false otherwise.
+ *
+ * @param exclusive True to exclusively handle touch events during scrolling,
+ * false to allow the target view to receive all touch events.
+ * @return The scroll helper, which may used to chain setter calls.
+ */
+ public AutoScrollHelper setExclusive(boolean exclusive) {
+ mExclusive = exclusive;
+ return this;
+ }
+
+ /**
+ * Indicates whether the scroll helper handles touch events exclusively
+ * during scrolling.
+ *
+ * @return True if exclusive handling of touch events during scrolling is
+ * enabled, false otherwise.
+ * @see #setExclusive(boolean)
+ */
+ public boolean isExclusive() {
+ return mExclusive;
+ }
+
+ /**
+ * Sets the absolute maximum scrolling velocity.
+ * <p>
+ * If relative velocity is not specified, scrolling will always reach the
+ * same maximum velocity. If both relative and maximum velocities are
+ * specified, the maximum velocity will be used to clamp the calculated
+ * relative velocity.
+ *
+ * @param horizontalMax The maximum horizontal scrolling velocity, or
+ * {@link #NO_MAX} to leave the relative value unconstrained.
+ * @param verticalMax The maximum vertical scrolling velocity, or
+ * {@link #NO_MAX} to leave the relative value unconstrained.
+ * @return The scroll helper, which may used to chain setter calls.
+ */
+ public AutoScrollHelper setMaximumVelocity(float horizontalMax, float verticalMax) {
+ mMaximumVelocity[HORIZONTAL] = horizontalMax / 1000f;
+ mMaximumVelocity[VERTICAL] = verticalMax / 1000f;
+ return this;
+ }
+
+ /**
+ * Sets the absolute minimum scrolling velocity.
+ * <p>
+ * If both relative and minimum velocities are specified, the minimum
+ * velocity will be used to clamp the calculated relative velocity.
+ *
+ * @param horizontalMin The minimum horizontal scrolling velocity, or
+ * {@link #NO_MIN} to leave the relative value unconstrained.
+ * @param verticalMin The minimum vertical scrolling velocity, or
+ * {@link #NO_MIN} to leave the relative value unconstrained.
+ * @return The scroll helper, which may used to chain setter calls.
+ */
+ public AutoScrollHelper setMinimumVelocity(float horizontalMin, float verticalMin) {
+ mMinimumVelocity[HORIZONTAL] = horizontalMin / 1000f;
+ mMinimumVelocity[VERTICAL] = verticalMin / 1000f;
+ return this;
+ }
+
+ /**
+ * Sets the target scrolling velocity relative to the host view's
+ * dimensions.
+ * <p>
+ * If both relative and maximum velocities are specified, the maximum
+ * velocity will be used to clamp the calculated relative velocity.
+ *
+ * @param horizontal The target horizontal velocity as a fraction of the
+ * host view width per second, or {@link #RELATIVE_UNSPECIFIED}
+ * to ignore.
+ * @param vertical The target vertical velocity as a fraction of the host
+ * view height per second, or {@link #RELATIVE_UNSPECIFIED} to
+ * ignore.
+ * @return The scroll helper, which may used to chain setter calls.
+ */
+ public AutoScrollHelper setRelativeVelocity(float horizontal, float vertical) {
+ mRelativeVelocity[HORIZONTAL] = horizontal / 1000f;
+ mRelativeVelocity[VERTICAL] = vertical / 1000f;
+ return this;
+ }
+
+ /**
+ * Sets the activation edge type, one of:
+ * <ul>
+ * <li>{@link #EDGE_TYPE_INSIDE} for edges that respond to touches inside
+ * the bounds of the host view. If touch moves outside the bounds, scrolling
+ * will stop.
+ * <li>{@link #EDGE_TYPE_INSIDE_EXTEND} for inside edges that continued to
+ * scroll when touch moves outside the bounds of the host view.
+ * <li>{@link #EDGE_TYPE_OUTSIDE} for edges that only respond to touches
+ * that move outside the bounds of the host view.
+ * </ul>
+ *
+ * @param type The type of edge to use.
+ * @return The scroll helper, which may used to chain setter calls.
+ */
+ public AutoScrollHelper setEdgeType(int type) {
+ mEdgeType = type;
+ return this;
+ }
+
+ /**
+ * Sets the activation edge size relative to the host view's dimensions.
+ * <p>
+ * If both relative and maximum edges are specified, the maximum edge will
+ * be used to constrain the calculated relative edge size.
+ *
+ * @param horizontal The horizontal edge size as a fraction of the host view
+ * width, or {@link #RELATIVE_UNSPECIFIED} to always use the
+ * maximum value.
+ * @param vertical The vertical edge size as a fraction of the host view
+ * height, or {@link #RELATIVE_UNSPECIFIED} to always use the
+ * maximum value.
+ * @return The scroll helper, which may used to chain setter calls.
+ */
+ public AutoScrollHelper setRelativeEdges(float horizontal, float vertical) {
+ mRelativeEdges[HORIZONTAL] = horizontal;
+ mRelativeEdges[VERTICAL] = vertical;
+ return this;
+ }
+
+ /**
+ * Sets the absolute maximum edge size.
+ * <p>
+ * If relative edge size is not specified, activation edges will always be
+ * the maximum edge size. If both relative and maximum edges are specified,
+ * the maximum edge will be used to constrain the calculated relative edge
+ * size.
+ *
+ * @param horizontalMax The maximum horizontal edge size in pixels, or
+ * {@link #NO_MAX} to use the unconstrained calculated relative
+ * value.
+ * @param verticalMax The maximum vertical edge size in pixels, or
+ * {@link #NO_MAX} to use the unconstrained calculated relative
+ * value.
+ * @return The scroll helper, which may used to chain setter calls.
+ */
+ public AutoScrollHelper setMaximumEdges(float horizontalMax, float verticalMax) {
+ mMaximumEdges[HORIZONTAL] = horizontalMax;
+ mMaximumEdges[VERTICAL] = verticalMax;
+ return this;
+ }
+
+ /**
+ * Sets the delay after entering an activation edge before activation of
+ * auto-scrolling. By default, the activation delay is set to
+ * {@link ViewConfiguration#getTapTimeout()}.
+ * <p>
+ * Specifying a delay of zero will start auto-scrolling immediately after
+ * the touch position enters an activation edge.
+ *
+ * @param delayMillis The activation delay in milliseconds.
+ * @return The scroll helper, which may used to chain setter calls.
+ */
+ public AutoScrollHelper setActivationDelay(int delayMillis) {
+ mActivationDelay = delayMillis;
+ return this;
+ }
+
+ /**
+ * Sets the amount of time after activation of auto-scrolling that is takes
+ * to reach target velocity for the current touch position.
+ * <p>
+ * Specifying a duration greater than zero prevents sudden jumps in
+ * velocity.
+ *
+ * @param durationMillis The ramp-up duration in milliseconds.
+ * @return The scroll helper, which may used to chain setter calls.
+ */
+ public AutoScrollHelper setRampUpDuration(int durationMillis) {
+ mScroller.setRampUpDuration(durationMillis);
+ return this;
+ }
+
+ /**
+ * Sets the amount of time after de-activation of auto-scrolling that is
+ * takes to slow to a stop.
+ * <p>
+ * Specifying a duration greater than zero prevents sudden jumps in
+ * velocity.
+ *
+ * @param durationMillis The ramp-down duration in milliseconds.
+ * @return The scroll helper, which may used to chain setter calls.
+ */
+ public AutoScrollHelper setRampDownDuration(int durationMillis) {
+ mScroller.setRampDownDuration(durationMillis);
+ return this;
+ }
+
+ /**
+ * Handles touch events by activating automatic scrolling, adjusting scroll
+ * velocity, or stopping.
+ * <p>
+ * If {@link #isExclusive()} is false, always returns false so that
+ * the host view may handle touch events. Otherwise, returns true when
+ * automatic scrolling is active and false otherwise.
+ */
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ if (!mEnabled) {
+ return false;
+ }
+
+ final int action = MotionEventCompat.getActionMasked(event);
+ switch (action) {
+ case MotionEvent.ACTION_DOWN:
+ mNeedsCancel = true;
+ mAlreadyDelayed = false;
+ // $FALL-THROUGH$
+ case MotionEvent.ACTION_MOVE:
+ final float xTargetVelocity = computeTargetVelocity(
+ HORIZONTAL, event.getX(), v.getWidth(), mTarget.getWidth());
+ final float yTargetVelocity = computeTargetVelocity(
+ VERTICAL, event.getY(), v.getHeight(), mTarget.getHeight());
+ mScroller.setTargetVelocity(xTargetVelocity, yTargetVelocity);
+
+ // If the auto scroller was not previously active, but it should
+ // be, then update the state and start animations.
+ if (!mAnimating && shouldAnimate()) {
+ startAnimating();
+ }
+ break;
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_CANCEL:
+ requestStop();
+ break;
+ }
+
+ return mExclusive && mAnimating;
+ }
+
+ /**
+ * @return whether the target is able to scroll in the requested direction
+ */
+ private boolean shouldAnimate() {
+ final ClampedScroller scroller = mScroller;
+ final int verticalDirection = scroller.getVerticalDirection();
+ final int horizontalDirection = scroller.getHorizontalDirection();
+
+ return verticalDirection != 0 && canTargetScrollVertically(verticalDirection)
+ || horizontalDirection != 0 && canTargetScrollHorizontally(horizontalDirection);
+ }
+
+ /**
+ * Starts the scroll animation.
+ */
+ private void startAnimating() {
+ if (mRunnable == null) {
+ mRunnable = new ScrollAnimationRunnable();
+ }
+
+ mAnimating = true;
+ mNeedsReset = true;
+
+ if (!mAlreadyDelayed && mActivationDelay > 0) {
+ ViewCompat.postOnAnimationDelayed(mTarget, mRunnable, mActivationDelay);
+ } else {
+ mRunnable.run();
+ }
+
+ // If we start animating again before the user lifts their finger, we
+ // already know it's not a tap and don't need an activation delay.
+ mAlreadyDelayed = true;
+ }
+
+ /**
+ * Requests that the scroll animation slow to a stop. If there is an
+ * activation delay, this may occur between posting the animation and
+ * actually running it.
+ */
+ private void requestStop() {
+ if (mNeedsReset) {
+ // The animation has been posted, but hasn't run yet. Manually
+ // stopping animation will prevent it from running.
+ mAnimating = false;
+ } else {
+ mScroller.requestStop();
+ }
+ }
+
+ private float computeTargetVelocity(
+ int direction, float coordinate, float srcSize, float dstSize) {
+ final float relativeEdge = mRelativeEdges[direction];
+ final float maximumEdge = mMaximumEdges[direction];
+ final float value = getEdgeValue(relativeEdge, srcSize, maximumEdge, coordinate);
+ if (value == 0) {
+ // The edge in this direction is not activated.
+ return 0;
+ }
+
+ final float relativeVelocity = mRelativeVelocity[direction];
+ final float minimumVelocity = mMinimumVelocity[direction];
+ final float maximumVelocity = mMaximumVelocity[direction];
+ final float targetVelocity = relativeVelocity * dstSize;
+
+ // Target velocity is adjusted for interpolated edge position, then
+ // clamped to the minimum and maximum values. Later, this value will be
+ // adjusted for time-based acceleration.
+ if (value > 0) {
+ return constrain(value * targetVelocity, minimumVelocity, maximumVelocity);
+ } else {
+ return -constrain(-value * targetVelocity, minimumVelocity, maximumVelocity);
+ }
+ }
+
+ /**
+ * Override this method to scroll the target view by the specified number of
+ * pixels.
+ *
+ * @param deltaX The number of pixels to scroll by horizontally.
+ * @param deltaY The number of pixels to scroll by vertically.
+ */
+ public abstract void scrollTargetBy(int deltaX, int deltaY);
+
+ /**
+ * Override this method to return whether the target view can be scrolled
+ * horizontally in a certain direction.
+ *
+ * @param direction Negative to check scrolling left, positive to check
+ * scrolling right.
+ * @return true if the target view is able to horizontally scroll in the
+ * specified direction.
+ */
+ public abstract boolean canTargetScrollHorizontally(int direction);
+
+ /**
+ * Override this method to return whether the target view can be scrolled
+ * vertically in a certain direction.
+ *
+ * @param direction Negative to check scrolling up, positive to check
+ * scrolling down.
+ * @return true if the target view is able to vertically scroll in the
+ * specified direction.
+ */
+ public abstract boolean canTargetScrollVertically(int direction);
+
+ /**
+ * Returns the interpolated position of a touch point relative to an edge
+ * defined by its relative inset, its maximum absolute inset, and the edge
+ * interpolator.
+ *
+ * @param relativeValue The size of the inset relative to the total size.
+ * @param size Total size.
+ * @param maxValue The maximum size of the inset, used to clamp (relative *
+ * total).
+ * @param current Touch position within within the total size.
+ * @return Interpolated value of the touch position within the edge.
+ */
+ private float getEdgeValue(float relativeValue, float size, float maxValue, float current) {
+ // For now, leading and trailing edges are always the same size.
+ final float edgeSize = constrain(relativeValue * size, NO_MIN, maxValue);
+ final float valueLeading = constrainEdgeValue(current, edgeSize);
+ final float valueTrailing = constrainEdgeValue(size - current, edgeSize);
+ final float value = (valueTrailing - valueLeading);
+ final float interpolated;
+ if (value < 0) {
+ interpolated = -mEdgeInterpolator.getInterpolation(-value);
+ } else if (value > 0) {
+ interpolated = mEdgeInterpolator.getInterpolation(value);
+ } else {
+ return 0;
+ }
+
+ return constrain(interpolated, -1, 1);
+ }
+
+ private float constrainEdgeValue(float current, float leading) {
+ if (leading == 0) {
+ return 0;
+ }
+
+ switch (mEdgeType) {
+ case EDGE_TYPE_INSIDE:
+ case EDGE_TYPE_INSIDE_EXTEND:
+ if (current < leading) {
+ if (current > 0) {
+ // Movement up to the edge is scaled.
+ return 1f - current / leading;
+ } else if (mAnimating && (mEdgeType == EDGE_TYPE_INSIDE_EXTEND)) {
+ // Movement beyond the edge is always maximum.
+ return 1f;
+ }
+ }
+ break;
+ case EDGE_TYPE_OUTSIDE:
+ if (current < 0) {
+ // Movement beyond the edge is scaled.
+ return current / -leading;
+ }
+ break;
+ }
+
+ return 0;
+ }
+
+ private static int constrain(int value, int min, int max) {
+ if (value > max) {
+ return max;
+ } else if (value < min) {
+ return min;
+ } else {
+ return value;
+ }
+ }
+
+ private static float constrain(float value, float min, float max) {
+ if (value > max) {
+ return max;
+ } else if (value < min) {
+ return min;
+ } else {
+ return value;
+ }
+ }
+
+ /**
+ * Sends a {@link MotionEvent#ACTION_CANCEL} event to the target view,
+ * canceling any ongoing touch events.
+ */
+ private void cancelTargetTouch() {
+ final long eventTime = SystemClock.uptimeMillis();
+ final MotionEvent cancel = MotionEvent.obtain(
+ eventTime, eventTime, MotionEvent.ACTION_CANCEL, 0, 0, 0);
+ mTarget.onTouchEvent(cancel);
+ cancel.recycle();
+ }
+
+ private class ScrollAnimationRunnable implements Runnable {
+ @Override
+ public void run() {
+ if (!mAnimating) {
+ return;
+ }
+
+ if (mNeedsReset) {
+ mNeedsReset = false;
+ mScroller.start();
+ }
+
+ final ClampedScroller scroller = mScroller;
+ if (scroller.isFinished() || !shouldAnimate()) {
+ mAnimating = false;
+ return;
+ }
+
+ if (mNeedsCancel) {
+ mNeedsCancel = false;
+ cancelTargetTouch();
+ }
+
+ scroller.computeScrollDelta();
+
+ final int deltaX = scroller.getDeltaX();
+ final int deltaY = scroller.getDeltaY();
+ scrollTargetBy(deltaX, deltaY);
+
+ // Keep going until the scroller has permanently stopped.
+ ViewCompat.postOnAnimation(mTarget, this);
+ }
+ }
+
+ /**
+ * Scroller whose velocity follows the curve of an {@link Interpolator} and
+ * is clamped to the interpolated 0f value before starting and the
+ * interpolated 1f value after a specified duration.
+ */
+ private static class ClampedScroller {
+ private int mRampUpDuration;
+ private int mRampDownDuration;
+ private float mTargetVelocityX;
+ private float mTargetVelocityY;
+
+ private long mStartTime;
+
+ private long mDeltaTime;
+ private int mDeltaX;
+ private int mDeltaY;
+
+ private long mStopTime;
+ private float mStopValue;
+ private int mEffectiveRampDown;
+
+ /**
+ * Creates a new ramp-up scroller that reaches full velocity after a
+ * specified duration.
+ */
+ public ClampedScroller() {
+ mStartTime = Long.MIN_VALUE;
+ mStopTime = -1;
+ mDeltaTime = 0;
+ mDeltaX = 0;
+ mDeltaY = 0;
+ }
+
+ public void setRampUpDuration(int durationMillis) {
+ mRampUpDuration = durationMillis;
+ }
+
+ public void setRampDownDuration(int durationMillis) {
+ mRampDownDuration = durationMillis;
+ }
+
+ /**
+ * Starts the scroller at the current animation time.
+ */
+ public void start() {
+ mStartTime = AnimationUtils.currentAnimationTimeMillis();
+ mStopTime = -1;
+ mDeltaTime = mStartTime;
+ mStopValue = 0.5f;
+ mDeltaX = 0;
+ mDeltaY = 0;
+ }
+
+ /**
+ * Stops the scroller at the current animation time.
+ */
+ public void requestStop() {
+ final long currentTime = AnimationUtils.currentAnimationTimeMillis();
+ mEffectiveRampDown = constrain((int) (currentTime - mStartTime), 0, mRampDownDuration);
+ mStopValue = getValueAt(currentTime);
+ mStopTime = currentTime;
+ }
+
+ public boolean isFinished() {
+ return mStopTime > 0
+ && AnimationUtils.currentAnimationTimeMillis() > mStopTime + mEffectiveRampDown;
+ }
+
+ private float getValueAt(long currentTime) {
+ if (currentTime < mStartTime) {
+ return 0f;
+ } else if (mStopTime < 0 || currentTime < mStopTime) {
+ final long elapsedSinceStart = currentTime - mStartTime;
+ return 0.5f * constrain(elapsedSinceStart / (float) mRampUpDuration, 0, 1);
+ } else {
+ final long elapsedSinceEnd = currentTime - mStopTime;
+ return (1 - mStopValue) + mStopValue
+ * constrain(elapsedSinceEnd / (float) mEffectiveRampDown, 0, 1);
+ }
+ }
+
+ /**
+ * Interpolates the value along a parabolic curve corresponding to the equation
+ * <code>y = -4x * (x-1)</code>.
+ *
+ * @param value The value to interpolate, between 0 and 1.
+ * @return the interpolated value, between 0 and 1.
+ */
+ private float interpolateValue(float value) {
+ return -4 * value * value + 4 * value;
+ }
+
+ /**
+ * Computes the current scroll deltas. This usually only be called after
+ * starting the scroller with {@link #start()}.
+ *
+ * @see #getDeltaX()
+ * @see #getDeltaY()
+ */
+ public void computeScrollDelta() {
+ if (mDeltaTime == 0) {
+ throw new RuntimeException("Cannot compute scroll delta before calling start()");
+ }
+
+ final long currentTime = AnimationUtils.currentAnimationTimeMillis();
+ final float value = getValueAt(currentTime);
+ final float scale = interpolateValue(value);
+ 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;
+ }
+
+ public int getHorizontalDirection() {
+ return (int) (mTargetVelocityX / Math.abs(mTargetVelocityX));
+ }
+
+ public int getVerticalDirection() {
+ return (int) (mTargetVelocityY / Math.abs(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;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/com/android/launcher3/AutoScroller.java b/src/com/android/launcher3/AutoScroller.java
deleted file mode 100644
index ac8e2e61a..000000000
--- a/src/com/android/launcher3/AutoScroller.java
+++ /dev/null
@@ -1,423 +0,0 @@
-/*
- * 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/Folder.java b/src/com/android/launcher3/Folder.java
index f57ff07a9..33d300cad 100644
--- a/src/com/android/launcher3/Folder.java
+++ b/src/com/android/launcher3/Folder.java
@@ -25,6 +25,7 @@ import android.content.res.Resources;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
+import android.os.SystemClock;
import android.text.InputType;
import android.text.Selection;
import android.text.Spannable;
@@ -107,8 +108,6 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
private float mFolderIconPivotX;
private float mFolderIconPivotY;
- private static final float MAX_SCROLL_VELOCITY = 1500f;
-
private boolean mIsEditingName = false;
private InputMethodManager mInputMethodManager;
@@ -121,7 +120,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
private boolean mDestroyed;
- private AutoScroller mAutoScroller;
+ private AutoScrollHelper mAutoScrollHelper;
private Runnable mDeferredAction;
private boolean mDeferDropAfterUninstall;
@@ -193,9 +192,7 @@ 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);
+ mAutoScrollHelper = new FolderAutoScrollHelper(mScrollView);
}
private ActionMode.Callback mActionModeCallback = new ActionMode.Callback() {
@@ -626,24 +623,29 @@ 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();
- float[] r = getDragViewVisualCenter(d.x, d.y, d.xOffset, d.yOffset, d.dragView, null);
+ final DragView dragView = d.dragView;
+ final int scrollOffset = mScrollView.getScrollY();
+ final float[] r = getDragViewVisualCenter(d.x, d.y, d.xOffset, d.yOffset, 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);
+ final long downTime = SystemClock.uptimeMillis();
+ final MotionEvent translatedEv = MotionEvent.obtain(
+ downTime, downTime, MotionEvent.ACTION_MOVE, d.x, d.y, 0);
+
+ if (!mAutoScrollHelper.isEnabled()) {
+ mAutoScrollHelper.setEnabled(true);
+ }
+
+ final boolean handled = mAutoScrollHelper.onTouch(this, translatedEv);
+ translatedEv.recycle();
+
+ if (handled) {
+ mReorderAlarm.cancelAlarm();
+ } else {
+ mTargetCell = mContent.findNearestArea(
+ (int) r[0], (int) r[1] + scrollOffset, 1, 1, mTargetCell);
if (isLayoutRtl()) {
mTargetCell[0] = mContent.getCountX() - mTargetCell[0] - 1;
}
@@ -658,8 +660,6 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
} else {
mDragMode = DRAG_MODE_NONE;
}
- } else {
- mReorderAlarm.cancelAlarm();
}
}
@@ -705,7 +705,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
public void onDragExit(DragObject d) {
// Exiting folder; stop the auto scroller.
- mAutoScroller.stop();
+ mAutoScrollHelper.setEnabled(false);
// 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) {
diff --git a/src/com/android/launcher3/FolderAutoScrollHelper.java b/src/com/android/launcher3/FolderAutoScrollHelper.java
new file mode 100644
index 000000000..d7e677053
--- /dev/null
+++ b/src/com/android/launcher3/FolderAutoScrollHelper.java
@@ -0,0 +1,56 @@
+/*
+ * 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.widget.ScrollView;
+
+/**
+ * An implementation of {@link AutoScrollHelper} that knows how to scroll
+ * through a {@link Folder}.
+ */
+public class FolderAutoScrollHelper extends AutoScrollHelper {
+ private static final float MAX_SCROLL_VELOCITY = 1500f;
+
+ private final ScrollView mTarget;
+
+ public FolderAutoScrollHelper(ScrollView target) {
+ super(target);
+
+ mTarget = target;
+
+ setActivationDelay(0);
+ setEdgeType(EDGE_TYPE_INSIDE);
+ setExclusive(true);
+ setMaximumVelocity(MAX_SCROLL_VELOCITY, MAX_SCROLL_VELOCITY);
+ }
+
+ @Override
+ public void scrollTargetBy(int deltaX, int deltaY) {
+ mTarget.scrollBy(deltaX, deltaY);
+ }
+
+ @Override
+ public boolean canTargetScrollHorizontally(int direction) {
+ // List do not scroll horizontally.
+ return false;
+ }
+
+ @Override
+ public boolean canTargetScrollVertically(int direction) {
+ return mTarget.canScrollVertically(direction);
+ }
+} \ No newline at end of file