1eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette/*
2eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette * Copyright (C) 2013 The Android Open Source Project
3eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette *
4eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette * Licensed under the Apache License, Version 2.0 (the "License");
5eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette * you may not use this file except in compliance with the License.
6eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette * You may obtain a copy of the License at
7eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette *
8eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette *      http://www.apache.org/licenses/LICENSE-2.0
9eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette *
10eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette * Unless required by applicable law or agreed to in writing, software
11eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette * distributed under the License is distributed on an "AS IS" BASIS,
12eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette * See the License for the specific language governing permissions and
14eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette * limitations under the License.
15eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette */
16eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette
17eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverettepackage android.support.v4.widget;
18eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette
19eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viveretteimport android.content.res.Resources;
206cf8db91596dd60eee4bb90925e4711cebb202d4Alan Viveretteimport android.os.SystemClock;
21eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viveretteimport android.support.v4.view.MotionEventCompat;
22eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viveretteimport android.support.v4.view.ViewCompat;
23eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viveretteimport android.util.DisplayMetrics;
24eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viveretteimport android.view.MotionEvent;
25eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viveretteimport android.view.View;
26eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viveretteimport android.view.ViewConfiguration;
27eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viveretteimport android.view.animation.AccelerateInterpolator;
28eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viveretteimport android.view.animation.AnimationUtils;
29eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viveretteimport android.view.animation.Interpolator;
30eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette
31eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette/**
32eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette * AutoScrollHelper is a utility class for adding automatic edge-triggered
33eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette * scrolling to Views.
34eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette * <p>
35eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette * <b>Note:</b> Implementing classes are responsible for overriding the
36504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette * {@link #scrollTargetBy}, {@link #canTargetScrollHorizontally}, and
37504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette * {@link #canTargetScrollVertically} methods. See
38eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette * {@link ListViewAutoScrollHelper} for a {@link android.widget.ListView}
39eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette * -specific implementation.
40eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette * <p>
41eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette * <h1>Activation</h1> Automatic scrolling starts when the user touches within
42eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette * an activation area. By default, activation areas are defined as the top,
43eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette * left, right, and bottom 20% of the host view's total area. Touching within
44eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette * the top activation area scrolls up, left scrolls to the left, and so on.
45eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette * <p>
46eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette * As the user touches closer to the extreme edge of the activation area,
47eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette * scrolling accelerates up to a maximum velocity. When using the default edge
48eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette * type, {@link #EDGE_TYPE_INSIDE_EXTEND}, moving outside of the view bounds
49eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette * will scroll at the maximum velocity.
50eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette * <p>
51eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette * The following activation properties may be configured:
52eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette * <ul>
53eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette * <li>Delay after entering activation area before auto-scrolling begins, see
54eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette * {@link #setActivationDelay}. Default value is
55eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette * {@link ViewConfiguration#getTapTimeout()} to avoid conflicting with taps.
56eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette * <li>Location of activation areas, see {@link #setEdgeType}. Default value is
57eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette * {@link #EDGE_TYPE_INSIDE_EXTEND}.
58eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette * <li>Size of activation areas relative to view size, see
59eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette * {@link #setRelativeEdges}. Default value is 20% for both vertical and
60eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette * horizontal edges.
61eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette * <li>Maximum size used to constrain relative size, see
62eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette * {@link #setMaximumEdges}. Default value is {@link #NO_MAX}.
63eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette * </ul>
64eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette * <h1>Scrolling</h1> When automatic scrolling is active, the helper will
65504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette * repeatedly call {@link #scrollTargetBy} to apply new scrolling offsets.
66eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette * <p>
67eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette * The following scrolling properties may be configured:
68eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette * <ul>
69eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette * <li>Acceleration ramp-up duration, see {@link #setRampUpDuration}. Default
702155135a266e288854714775d8a9ba7d8eddfd2aMindy Pereira * value is 500 milliseconds.
71504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette * <li>Acceleration ramp-down duration, see {@link #setRampDownDuration}.
72504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette * Default value is 500 milliseconds.
73eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette * <li>Target velocity relative to view size, see {@link #setRelativeVelocity}.
74eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette * Default value is 100% per second for both vertical and horizontal.
75eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette * <li>Minimum velocity used to constrain relative velocity, see
76eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette * {@link #setMinimumVelocity}. When set, scrolling will accelerate to the
77eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette * larger of either this value or the relative target value. Default value is
78eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette * approximately 5 centimeters or 315 dips per second.
79eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette * <li>Maximum velocity used to constrain relative velocity, see
80eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette * {@link #setMaximumVelocity}. Default value is approximately 25 centimeters or
81eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette * 1575 dips per second.
82eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette * </ul>
83eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette */
84eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverettepublic abstract class AutoScrollHelper implements View.OnTouchListener {
85eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    /**
86eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * Constant passed to {@link #setRelativeEdges} or
87eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * {@link #setRelativeVelocity}. Using this value ensures that the computed
88eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * relative value is ignored and the absolute maximum value is always used.
89eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     */
90eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    public static final float RELATIVE_UNSPECIFIED = 0;
91eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette
92eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    /**
93eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * Constant passed to {@link #setMaximumEdges}, {@link #setMaximumVelocity},
94eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * or {@link #setMinimumVelocity}. Using this value ensures that the
95eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * computed relative value is always used without constraining to a
96eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * particular minimum or maximum value.
97eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     */
98eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    public static final float NO_MAX = Float.MAX_VALUE;
99eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette
100eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    /**
101eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * Constant passed to {@link #setMaximumEdges}, or
102eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * {@link #setMaximumVelocity}, or {@link #setMinimumVelocity}. Using this
103eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * value ensures that the computed relative value is always used without
104eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * constraining to a particular minimum or maximum value.
105eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     */
106eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    public static final float NO_MIN = 0;
107eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette
108eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    /**
109eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * Edge type that specifies an activation area starting at the view bounds
110eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * and extending inward. Moving outside the view bounds will stop scrolling.
111eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     *
112eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * @see #setEdgeType
113eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     */
114eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    public static final int EDGE_TYPE_INSIDE = 0;
115eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette
116eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    /**
117eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * Edge type that specifies an activation area starting at the view bounds
118eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * and extending inward. After activation begins, moving outside the view
119eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * bounds will continue scrolling.
120eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     *
121eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * @see #setEdgeType
122eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     */
123eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    public static final int EDGE_TYPE_INSIDE_EXTEND = 1;
124eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette
125eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    /**
126eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * Edge type that specifies an activation area starting at the view bounds
127eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * and extending outward. Moving inside the view bounds will stop scrolling.
128eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     *
129eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * @see #setEdgeType
130eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     */
131eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    public static final int EDGE_TYPE_OUTSIDE = 2;
132eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette
133eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    private static final int HORIZONTAL = 0;
134eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    private static final int VERTICAL = 1;
135eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette
136eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    /** Scroller used to control acceleration toward maximum velocity. */
137eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    private final ClampedScroller mScroller = new ClampedScroller();
138eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette
139eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    /** Interpolator used to scale velocity with touch position. */
140eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    private final Interpolator mEdgeInterpolator = new AccelerateInterpolator();
141eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette
142eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    /** The view to auto-scroll. Might not be the source of touch events. */
143eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    private final View mTarget;
144eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette
145eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    /** Runnable used to animate scrolling. */
146eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    private Runnable mRunnable;
147eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette
148eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    /** Edge insets used to activate auto-scrolling. */
149eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    private float[] mRelativeEdges = new float[] { RELATIVE_UNSPECIFIED, RELATIVE_UNSPECIFIED };
150eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette
151eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    /** Clamping values for edge insets used to activate auto-scrolling. */
152eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    private float[] mMaximumEdges = new float[] { NO_MAX, NO_MAX };
153eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette
154eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    /** The type of edge being used. */
155eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    private int mEdgeType;
156eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette
157eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    /** Delay after entering an activation edge before auto-scrolling begins. */
158eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    private int mActivationDelay;
159eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette
160eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    /** Relative scrolling velocity at maximum edge distance. */
161eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    private float[] mRelativeVelocity = new float[] { RELATIVE_UNSPECIFIED, RELATIVE_UNSPECIFIED };
162eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette
163eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    /** Clamping values used for scrolling velocity. */
164eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    private float[] mMinimumVelocity = new float[] { NO_MIN, NO_MIN };
165eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette
166eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    /** Clamping values used for scrolling velocity. */
167eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    private float[] mMaximumVelocity = new float[] { NO_MAX, NO_MAX };
168eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette
169eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    /** Whether to start activation immediately. */
170504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette    private boolean mAlreadyDelayed;
171eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette
172eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    /** Whether to reset the scroller start time on the next animation. */
173504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette    private boolean mNeedsReset;
174eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette
175504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette    /** Whether to send a cancel motion event to the target view. */
176504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette    private boolean mNeedsCancel;
177eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette
178504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette    /** Whether the auto-scroller is actively scrolling. */
179504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette    private boolean mAnimating;
1806cf8db91596dd60eee4bb90925e4711cebb202d4Alan Viverette
181eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    /** Whether the auto-scroller is enabled. */
182eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    private boolean mEnabled;
183eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette
1846cf8db91596dd60eee4bb90925e4711cebb202d4Alan Viverette    /** Whether the auto-scroller consumes events when scrolling. */
185504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette    private boolean mExclusive;
1866cf8db91596dd60eee4bb90925e4711cebb202d4Alan Viverette
187eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    // Default values.
188eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    private static final int DEFAULT_EDGE_TYPE = EDGE_TYPE_INSIDE_EXTEND;
189eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    private static final int DEFAULT_MINIMUM_VELOCITY_DIPS = 315;
190eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    private static final int DEFAULT_MAXIMUM_VELOCITY_DIPS = 1575;
191eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    private static final float DEFAULT_MAXIMUM_EDGE = NO_MAX;
192eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    private static final float DEFAULT_RELATIVE_EDGE = 0.2f;
193eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    private static final float DEFAULT_RELATIVE_VELOCITY = 1f;
194eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    private static final int DEFAULT_ACTIVATION_DELAY = ViewConfiguration.getTapTimeout();
1952155135a266e288854714775d8a9ba7d8eddfd2aMindy Pereira    private static final int DEFAULT_RAMP_UP_DURATION = 500;
196504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette    private static final int DEFAULT_RAMP_DOWN_DURATION = 500;
197eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette
198eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    /**
199eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * Creates a new helper for scrolling the specified target view.
200eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * <p>
201eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * The resulting helper may be configured by chaining setter calls and
202eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * should be set as a touch listener on the target view.
203eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * <p>
204eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * By default, the helper is disabled and will not respond to touch events
205eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * until it is enabled using {@link #setEnabled}.
206eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     *
207eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * @param target The view to automatically scroll.
208eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     */
209eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    public AutoScrollHelper(View target) {
210eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        mTarget = target;
211eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette
212eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        final DisplayMetrics metrics = Resources.getSystem().getDisplayMetrics();
213eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        final int maxVelocity = (int) (DEFAULT_MAXIMUM_VELOCITY_DIPS * metrics.density + 0.5f);
214eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        final int minVelocity = (int) (DEFAULT_MINIMUM_VELOCITY_DIPS * metrics.density + 0.5f);
215eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        setMaximumVelocity(maxVelocity, maxVelocity);
216eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        setMinimumVelocity(minVelocity, minVelocity);
217eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette
218eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        setEdgeType(DEFAULT_EDGE_TYPE);
219eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        setMaximumEdges(DEFAULT_MAXIMUM_EDGE, DEFAULT_MAXIMUM_EDGE);
220eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        setRelativeEdges(DEFAULT_RELATIVE_EDGE, DEFAULT_RELATIVE_EDGE);
221eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        setRelativeVelocity(DEFAULT_RELATIVE_VELOCITY, DEFAULT_RELATIVE_VELOCITY);
222eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        setActivationDelay(DEFAULT_ACTIVATION_DELAY);
223eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        setRampUpDuration(DEFAULT_RAMP_UP_DURATION);
224504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette        setRampDownDuration(DEFAULT_RAMP_DOWN_DURATION);
225eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    }
226eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette
227eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    /**
228eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * Sets whether the scroll helper is enabled and should respond to touch
229eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * events.
230eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     *
231eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * @param enabled Whether the scroll helper is enabled.
232eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * @return The scroll helper, which may used to chain setter calls.
233eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     */
234eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    public AutoScrollHelper setEnabled(boolean enabled) {
235504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette        if (mEnabled && !enabled) {
236504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette            requestStop();
237eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        }
238eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette
239eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        mEnabled = enabled;
240eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        return this;
241eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    }
242eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette
243eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    /**
244eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * @return True if this helper is enabled and responding to touch events.
245eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     */
246eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    public boolean isEnabled() {
247eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        return mEnabled;
248eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    }
249eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette
250eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    /**
2516cf8db91596dd60eee4bb90925e4711cebb202d4Alan Viverette     * Enables or disables exclusive handling of touch events during scrolling.
2526cf8db91596dd60eee4bb90925e4711cebb202d4Alan Viverette     * By default, exclusive handling is disabled and the target view receives
2536cf8db91596dd60eee4bb90925e4711cebb202d4Alan Viverette     * all touch events.
2546cf8db91596dd60eee4bb90925e4711cebb202d4Alan Viverette     * <p>
2556cf8db91596dd60eee4bb90925e4711cebb202d4Alan Viverette     * When enabled, {@link #onTouch} will return true if the helper is
2566cf8db91596dd60eee4bb90925e4711cebb202d4Alan Viverette     * currently scrolling and false otherwise.
2576cf8db91596dd60eee4bb90925e4711cebb202d4Alan Viverette     *
258504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette     * @param exclusive True to exclusively handle touch events during scrolling,
2596cf8db91596dd60eee4bb90925e4711cebb202d4Alan Viverette     *            false to allow the target view to receive all touch events.
260504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette     * @return The scroll helper, which may used to chain setter calls.
2616cf8db91596dd60eee4bb90925e4711cebb202d4Alan Viverette     */
262504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette    public AutoScrollHelper setExclusive(boolean exclusive) {
263504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette        mExclusive = exclusive;
264504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette        return this;
2656cf8db91596dd60eee4bb90925e4711cebb202d4Alan Viverette    }
2666cf8db91596dd60eee4bb90925e4711cebb202d4Alan Viverette
2676cf8db91596dd60eee4bb90925e4711cebb202d4Alan Viverette    /**
2686cf8db91596dd60eee4bb90925e4711cebb202d4Alan Viverette     * Indicates whether the scroll helper handles touch events exclusively
2696cf8db91596dd60eee4bb90925e4711cebb202d4Alan Viverette     * during scrolling.
2706cf8db91596dd60eee4bb90925e4711cebb202d4Alan Viverette     *
2716cf8db91596dd60eee4bb90925e4711cebb202d4Alan Viverette     * @return True if exclusive handling of touch events during scrolling is
2726cf8db91596dd60eee4bb90925e4711cebb202d4Alan Viverette     *         enabled, false otherwise.
273504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette     * @see #setExclusive(boolean)
2746cf8db91596dd60eee4bb90925e4711cebb202d4Alan Viverette     */
275504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette    public boolean isExclusive() {
276504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette        return mExclusive;
2776cf8db91596dd60eee4bb90925e4711cebb202d4Alan Viverette    }
2786cf8db91596dd60eee4bb90925e4711cebb202d4Alan Viverette
2796cf8db91596dd60eee4bb90925e4711cebb202d4Alan Viverette    /**
280eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * Sets the absolute maximum scrolling velocity.
281eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * <p>
282eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * If relative velocity is not specified, scrolling will always reach the
283eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * same maximum velocity. If both relative and maximum velocities are
284eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * specified, the maximum velocity will be used to clamp the calculated
285eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * relative velocity.
286eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     *
287eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * @param horizontalMax The maximum horizontal scrolling velocity, or
288eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     *            {@link #NO_MAX} to leave the relative value unconstrained.
289eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * @param verticalMax The maximum vertical scrolling velocity, or
290eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     *            {@link #NO_MAX} to leave the relative value unconstrained.
291eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * @return The scroll helper, which may used to chain setter calls.
292eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     */
293eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    public AutoScrollHelper setMaximumVelocity(float horizontalMax, float verticalMax) {
294eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        mMaximumVelocity[HORIZONTAL] = horizontalMax / 1000f;
295eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        mMaximumVelocity[VERTICAL] = verticalMax / 1000f;
296eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        return this;
297eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    }
298eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette
299eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    /**
300eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * Sets the absolute minimum scrolling velocity.
301eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * <p>
302eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * If both relative and minimum velocities are specified, the minimum
303eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * velocity will be used to clamp the calculated relative velocity.
304eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     *
305eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * @param horizontalMin The minimum horizontal scrolling velocity, or
306eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     *            {@link #NO_MIN} to leave the relative value unconstrained.
307eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * @param verticalMin The minimum vertical scrolling velocity, or
308eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     *            {@link #NO_MIN} to leave the relative value unconstrained.
309eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * @return The scroll helper, which may used to chain setter calls.
310eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     */
311eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    public AutoScrollHelper setMinimumVelocity(float horizontalMin, float verticalMin) {
312eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        mMinimumVelocity[HORIZONTAL] = horizontalMin / 1000f;
313eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        mMinimumVelocity[VERTICAL] = verticalMin / 1000f;
314eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        return this;
315eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    }
316eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette
317eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    /**
318eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * Sets the target scrolling velocity relative to the host view's
319eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * dimensions.
320eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * <p>
321eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * If both relative and maximum velocities are specified, the maximum
322eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * velocity will be used to clamp the calculated relative velocity.
323eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     *
324eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * @param horizontal The target horizontal velocity as a fraction of the
325eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     *            host view width per second, or {@link #RELATIVE_UNSPECIFIED}
326eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     *            to ignore.
327eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * @param vertical The target vertical velocity as a fraction of the host
328eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     *            view height per second, or {@link #RELATIVE_UNSPECIFIED} to
329eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     *            ignore.
330eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * @return The scroll helper, which may used to chain setter calls.
331eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     */
332eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    public AutoScrollHelper setRelativeVelocity(float horizontal, float vertical) {
333eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        mRelativeVelocity[HORIZONTAL] = horizontal / 1000f;
334eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        mRelativeVelocity[VERTICAL] = vertical / 1000f;
335eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        return this;
336eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    }
337eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette
338eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    /**
339eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * Sets the activation edge type, one of:
340eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * <ul>
341eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * <li>{@link #EDGE_TYPE_INSIDE} for edges that respond to touches inside
342eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * the bounds of the host view. If touch moves outside the bounds, scrolling
343eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * will stop.
344eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * <li>{@link #EDGE_TYPE_INSIDE_EXTEND} for inside edges that continued to
345eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * scroll when touch moves outside the bounds of the host view.
346eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * <li>{@link #EDGE_TYPE_OUTSIDE} for edges that only respond to touches
347eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * that move outside the bounds of the host view.
348eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * </ul>
349eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     *
350eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * @param type The type of edge to use.
351eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * @return The scroll helper, which may used to chain setter calls.
352eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     */
353eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    public AutoScrollHelper setEdgeType(int type) {
354eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        mEdgeType = type;
355eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        return this;
356eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    }
357eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette
358eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    /**
359eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * Sets the activation edge size relative to the host view's dimensions.
360eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * <p>
361eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * If both relative and maximum edges are specified, the maximum edge will
362eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * be used to constrain the calculated relative edge size.
363eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     *
364eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * @param horizontal The horizontal edge size as a fraction of the host view
365eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     *            width, or {@link #RELATIVE_UNSPECIFIED} to always use the
366eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     *            maximum value.
367eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * @param vertical The vertical edge size as a fraction of the host view
368eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     *            height, or {@link #RELATIVE_UNSPECIFIED} to always use the
369eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     *            maximum value.
370eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * @return The scroll helper, which may used to chain setter calls.
371eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     */
372eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    public AutoScrollHelper setRelativeEdges(float horizontal, float vertical) {
373eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        mRelativeEdges[HORIZONTAL] = horizontal;
374eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        mRelativeEdges[VERTICAL] = vertical;
375eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        return this;
376eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    }
377eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette
378eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    /**
379eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * Sets the absolute maximum edge size.
380eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * <p>
381eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * If relative edge size is not specified, activation edges will always be
382eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * the maximum edge size. If both relative and maximum edges are specified,
383eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * the maximum edge will be used to constrain the calculated relative edge
384eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * size.
385eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     *
386eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * @param horizontalMax The maximum horizontal edge size in pixels, or
387eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     *            {@link #NO_MAX} to use the unconstrained calculated relative
388eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     *            value.
389eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * @param verticalMax The maximum vertical edge size in pixels, or
390eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     *            {@link #NO_MAX} to use the unconstrained calculated relative
391eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     *            value.
392eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * @return The scroll helper, which may used to chain setter calls.
393eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     */
394eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    public AutoScrollHelper setMaximumEdges(float horizontalMax, float verticalMax) {
395eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        mMaximumEdges[HORIZONTAL] = horizontalMax;
396eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        mMaximumEdges[VERTICAL] = verticalMax;
397eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        return this;
398eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    }
399eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette
400eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    /**
401eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * Sets the delay after entering an activation edge before activation of
402eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * auto-scrolling. By default, the activation delay is set to
403eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * {@link ViewConfiguration#getTapTimeout()}.
404eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * <p>
405eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * Specifying a delay of zero will start auto-scrolling immediately after
406eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * the touch position enters an activation edge.
407eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     *
408eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * @param delayMillis The activation delay in milliseconds.
409eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * @return The scroll helper, which may used to chain setter calls.
410eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     */
411eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    public AutoScrollHelper setActivationDelay(int delayMillis) {
412eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        mActivationDelay = delayMillis;
413eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        return this;
414eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    }
415eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette
416eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    /**
417eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * Sets the amount of time after activation of auto-scrolling that is takes
418eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * to reach target velocity for the current touch position.
419eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * <p>
420eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * Specifying a duration greater than zero prevents sudden jumps in
421eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * velocity.
422eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     *
423eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * @param durationMillis The ramp-up duration in milliseconds.
424eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * @return The scroll helper, which may used to chain setter calls.
425eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     */
426eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    public AutoScrollHelper setRampUpDuration(int durationMillis) {
427504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette        mScroller.setRampUpDuration(durationMillis);
428504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette        return this;
429504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette    }
430504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette
431504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette    /**
432504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette     * Sets the amount of time after de-activation of auto-scrolling that is
433504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette     * takes to slow to a stop.
434504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette     * <p>
435504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette     * Specifying a duration greater than zero prevents sudden jumps in
436504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette     * velocity.
437504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette     *
438504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette     * @param durationMillis The ramp-down duration in milliseconds.
439504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette     * @return The scroll helper, which may used to chain setter calls.
440504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette     */
441504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette    public AutoScrollHelper setRampDownDuration(int durationMillis) {
442504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette        mScroller.setRampDownDuration(durationMillis);
443eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        return this;
444eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    }
445eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette
446eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    /**
447eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * Handles touch events by activating automatic scrolling, adjusting scroll
4486cf8db91596dd60eee4bb90925e4711cebb202d4Alan Viverette     * velocity, or stopping.
4496cf8db91596dd60eee4bb90925e4711cebb202d4Alan Viverette     * <p>
450504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette     * If {@link #isExclusive()} is false, always returns false so that
4516cf8db91596dd60eee4bb90925e4711cebb202d4Alan Viverette     * the host view may handle touch events. Otherwise, returns true when
4526cf8db91596dd60eee4bb90925e4711cebb202d4Alan Viverette     * automatic scrolling is active and false otherwise.
453eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     */
454eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    @Override
455eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    public boolean onTouch(View v, MotionEvent event) {
456eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        if (!mEnabled) {
457eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette            return false;
458eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        }
459eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette
460eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        final int action = MotionEventCompat.getActionMasked(event);
461eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        switch (action) {
462eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette            case MotionEvent.ACTION_DOWN:
463504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette                mNeedsCancel = true;
464504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette                mAlreadyDelayed = false;
465504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette                // $FALL-THROUGH$
466eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette            case MotionEvent.ACTION_MOVE:
467504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette                final float xTargetVelocity = computeTargetVelocity(
468504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette                        HORIZONTAL, event.getX(), v.getWidth(), mTarget.getWidth());
469504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette                final float yTargetVelocity = computeTargetVelocity(
470504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette                        VERTICAL, event.getY(), v.getHeight(), mTarget.getHeight());
471504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette                mScroller.setTargetVelocity(xTargetVelocity, yTargetVelocity);
472504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette
473504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette                // If the auto scroller was not previously active, but it should
474504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette                // be, then update the state and start animations.
475504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette                if (!mAnimating && shouldAnimate()) {
476504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette                    startAnimating();
477eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette                }
478eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette                break;
479eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette            case MotionEvent.ACTION_UP:
480eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette            case MotionEvent.ACTION_CANCEL:
481504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette                requestStop();
482eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette                break;
483eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        }
484eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette
485504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette        return mExclusive && mAnimating;
486eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    }
487eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette
488eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    /**
489504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette     * @return whether the target is able to scroll in the requested direction
490504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette     */
491504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette    private boolean shouldAnimate() {
492504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette        final ClampedScroller scroller = mScroller;
493504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette        final int verticalDirection = scroller.getVerticalDirection();
494504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette        final int horizontalDirection = scroller.getHorizontalDirection();
495504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette
496504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette        return verticalDirection != 0 && canTargetScrollVertically(verticalDirection)
497504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette                || horizontalDirection != 0 && canTargetScrollHorizontally(horizontalDirection);
498504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette    }
499504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette
500504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette    /**
501504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette     * Starts the scroll animation.
502504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette     */
503504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette    private void startAnimating() {
504504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette        if (mRunnable == null) {
505504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette            mRunnable = new ScrollAnimationRunnable();
506504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette        }
507504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette
508504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette        mAnimating = true;
509504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette        mNeedsReset = true;
510504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette
511504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette        if (!mAlreadyDelayed && mActivationDelay > 0) {
512504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette            ViewCompat.postOnAnimationDelayed(mTarget, mRunnable, mActivationDelay);
513504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette        } else {
514504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette            mRunnable.run();
515504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette        }
516504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette
517504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette        // If we start animating again before the user lifts their finger, we
518504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette        // already know it's not a tap and don't need an activation delay.
519504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette        mAlreadyDelayed = true;
520504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette    }
521504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette
522504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette    /**
523504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette     * Requests that the scroll animation slow to a stop. If there is an
524504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette     * activation delay, this may occur between posting the animation and
525504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette     * actually running it.
526504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette     */
527504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette    private void requestStop() {
528504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette        if (mNeedsReset) {
529504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette            // The animation has been posted, but hasn't run yet. Manually
530504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette            // stopping animation will prevent it from running.
531504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette            mAnimating = false;
532504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette        } else {
533504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette            mScroller.requestStop();
534504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette        }
535504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette    }
536504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette
537504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette    private float computeTargetVelocity(
538504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette            int direction, float coordinate, float srcSize, float dstSize) {
539504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette        final float relativeEdge = mRelativeEdges[direction];
540504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette        final float maximumEdge = mMaximumEdges[direction];
541504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette        final float value = getEdgeValue(relativeEdge, srcSize, maximumEdge, coordinate);
542504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette        if (value == 0) {
543504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette            // The edge in this direction is not activated.
544504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette            return 0;
545504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette        }
546504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette
547504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette        final float relativeVelocity = mRelativeVelocity[direction];
548504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette        final float minimumVelocity = mMinimumVelocity[direction];
549504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette        final float maximumVelocity = mMaximumVelocity[direction];
550504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette        final float targetVelocity = relativeVelocity * dstSize;
551504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette
552504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette        // Target velocity is adjusted for interpolated edge position, then
553504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette        // clamped to the minimum and maximum values. Later, this value will be
554504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette        // adjusted for time-based acceleration.
555504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette        if (value > 0) {
556504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette            return constrain(value * targetVelocity, minimumVelocity, maximumVelocity);
557504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette        } else {
558504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette            return -constrain(-value * targetVelocity, minimumVelocity, maximumVelocity);
559504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette        }
560504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette    }
561504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette
562504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette    /**
563504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette     * Override this method to scroll the target view by the specified number of
564504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette     * pixels.
565504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette     *
566504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette     * @param deltaX The number of pixels to scroll by horizontally.
567504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette     * @param deltaY The number of pixels to scroll by vertically.
568504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette     */
569504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette    public abstract void scrollTargetBy(int deltaX, int deltaY);
570504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette
571504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette    /**
572504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette     * Override this method to return whether the target view can be scrolled
573504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette     * horizontally in a certain direction.
574504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette     *
575504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette     * @param direction Negative to check scrolling left, positive to check
576504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette     *            scrolling right.
577504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette     * @return true if the target view is able to horizontally scroll in the
578504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette     *         specified direction.
579504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette     */
580504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette    public abstract boolean canTargetScrollHorizontally(int direction);
581504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette
582504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette    /**
583504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette     * Override this method to return whether the target view can be scrolled
584504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette     * vertically in a certain direction.
585eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     *
586504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette     * @param direction Negative to check scrolling up, positive to check
587504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette     *            scrolling down.
588504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette     * @return true if the target view is able to vertically scroll in the
589504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette     *         specified direction.
590eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     */
591504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette    public abstract boolean canTargetScrollVertically(int direction);
592eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette
593eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    /**
594eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * Returns the interpolated position of a touch point relative to an edge
595eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * defined by its relative inset, its maximum absolute inset, and the edge
596eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * interpolator.
597eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     *
598eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * @param relativeValue The size of the inset relative to the total size.
599eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * @param size Total size.
600eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * @param maxValue The maximum size of the inset, used to clamp (relative *
601eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     *            total).
602eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * @param current Touch position within within the total size.
603eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * @return Interpolated value of the touch position within the edge.
604eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     */
605eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    private float getEdgeValue(float relativeValue, float size, float maxValue, float current) {
606eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        // For now, leading and trailing edges are always the same size.
607eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        final float edgeSize = constrain(relativeValue * size, NO_MIN, maxValue);
608eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        final float valueLeading = constrainEdgeValue(current, edgeSize);
609eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        final float valueTrailing = constrainEdgeValue(size - current, edgeSize);
610eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        final float value = (valueTrailing - valueLeading);
611eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        final float interpolated;
612eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        if (value < 0) {
613eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette            interpolated = -mEdgeInterpolator.getInterpolation(-value);
614eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        } else if (value > 0) {
615eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette            interpolated = mEdgeInterpolator.getInterpolation(value);
616eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        } else {
617eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette            return 0;
618eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        }
619eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette
620eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        return constrain(interpolated, -1, 1);
621eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    }
622eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette
623eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    private float constrainEdgeValue(float current, float leading) {
624eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        if (leading == 0) {
625eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette            return 0;
626eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        }
627eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette
628eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        switch (mEdgeType) {
629eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette            case EDGE_TYPE_INSIDE:
630eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette            case EDGE_TYPE_INSIDE_EXTEND:
631eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette                if (current < leading) {
6327b1a5a45acedd56616807d5c4b3acc17fbfc92adMindy Pereira                    if (current >= 0) {
633eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette                        // Movement up to the edge is scaled.
634eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette                        return 1f - current / leading;
635504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette                    } else if (mAnimating && (mEdgeType == EDGE_TYPE_INSIDE_EXTEND)) {
636eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette                        // Movement beyond the edge is always maximum.
637eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette                        return 1f;
638eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette                    }
639eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette                }
640eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette                break;
641eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette            case EDGE_TYPE_OUTSIDE:
642eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette                if (current < 0) {
643eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette                    // Movement beyond the edge is scaled.
644eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette                    return current / -leading;
645eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette                }
646eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette                break;
647eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        }
648eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette
649eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        return 0;
650eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    }
651eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette
652504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette    private static int constrain(int value, int min, int max) {
653eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        if (value > max) {
654eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette            return max;
655eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        } else if (value < min) {
656eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette            return min;
657eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        } else {
658eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette            return value;
659eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        }
660eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    }
661eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette
662504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette    private static float constrain(float value, float min, float max) {
663504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette        if (value > max) {
664504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette            return max;
665504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette        } else if (value < min) {
666504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette            return min;
667504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette        } else {
668504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette            return value;
669eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        }
670eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    }
671eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette
6726cf8db91596dd60eee4bb90925e4711cebb202d4Alan Viverette    /**
6736cf8db91596dd60eee4bb90925e4711cebb202d4Alan Viverette     * Sends a {@link MotionEvent#ACTION_CANCEL} event to the target view,
6746cf8db91596dd60eee4bb90925e4711cebb202d4Alan Viverette     * canceling any ongoing touch events.
6756cf8db91596dd60eee4bb90925e4711cebb202d4Alan Viverette     */
6766cf8db91596dd60eee4bb90925e4711cebb202d4Alan Viverette    private void cancelTargetTouch() {
677504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette        final long eventTime = SystemClock.uptimeMillis();
6786cf8db91596dd60eee4bb90925e4711cebb202d4Alan Viverette        final MotionEvent cancel = MotionEvent.obtain(
679504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette                eventTime, eventTime, MotionEvent.ACTION_CANCEL, 0, 0, 0);
6806cf8db91596dd60eee4bb90925e4711cebb202d4Alan Viverette        mTarget.onTouchEvent(cancel);
6816cf8db91596dd60eee4bb90925e4711cebb202d4Alan Viverette        cancel.recycle();
6826cf8db91596dd60eee4bb90925e4711cebb202d4Alan Viverette    }
6836cf8db91596dd60eee4bb90925e4711cebb202d4Alan Viverette
684504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette    private class ScrollAnimationRunnable implements Runnable {
685eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        @Override
686eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        public void run() {
687504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette            if (!mAnimating) {
688eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette                return;
689eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette            }
690eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette
691504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette            if (mNeedsReset) {
692504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette                mNeedsReset = false;
693eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette                mScroller.start();
694eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette            }
695eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette
696eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette            final ClampedScroller scroller = mScroller;
697504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette            if (scroller.isFinished() || !shouldAnimate()) {
698504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette                mAnimating = false;
699504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette                return;
700504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette            }
701504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette
702504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette            if (mNeedsCancel) {
703504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette                mNeedsCancel = false;
704504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette                cancelTargetTouch();
705504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette            }
706504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette
707eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette            scroller.computeScrollDelta();
708eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette
709eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette            final int deltaX = scroller.getDeltaX();
710eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette            final int deltaY = scroller.getDeltaY();
711504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette            scrollTargetBy(deltaX,  deltaY);
7126cf8db91596dd60eee4bb90925e4711cebb202d4Alan Viverette
713504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette            // Keep going until the scroller has permanently stopped.
714504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette            ViewCompat.postOnAnimation(mTarget, this);
715eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        }
716eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    }
717eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette
718eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    /**
719eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * Scroller whose velocity follows the curve of an {@link Interpolator} and
720eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * is clamped to the interpolated 0f value before starting and the
721eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     * interpolated 1f value after a specified duration.
722eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette     */
723eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    private static class ClampedScroller {
724504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette        private int mRampUpDuration;
725504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette        private int mRampDownDuration;
726eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        private float mTargetVelocityX;
727eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        private float mTargetVelocityY;
728eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette
729eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        private long mStartTime;
730504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette
731eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        private long mDeltaTime;
732eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        private int mDeltaX;
733eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        private int mDeltaY;
734eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette
735504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette        private long mStopTime;
736504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette        private float mStopValue;
737504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette        private int mEffectiveRampDown;
738504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette
739eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        /**
740eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette         * Creates a new ramp-up scroller that reaches full velocity after a
741eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette         * specified duration.
742eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette         */
743eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        public ClampedScroller() {
744504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette            mStartTime = Long.MIN_VALUE;
745504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette            mStopTime = -1;
746504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette            mDeltaTime = 0;
747504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette            mDeltaX = 0;
748504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette            mDeltaY = 0;
749eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        }
750eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette
751504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette        public void setRampUpDuration(int durationMillis) {
752504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette            mRampUpDuration = durationMillis;
753504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette        }
754504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette
755504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette        public void setRampDownDuration(int durationMillis) {
756504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette            mRampDownDuration = durationMillis;
757eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        }
758eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette
759eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        /**
760eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette         * Starts the scroller at the current animation time.
761eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette         */
762eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        public void start() {
763eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette            mStartTime = AnimationUtils.currentAnimationTimeMillis();
764504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette            mStopTime = -1;
765eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette            mDeltaTime = mStartTime;
766504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette            mStopValue = 0.5f;
767504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette            mDeltaX = 0;
768504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette            mDeltaY = 0;
769eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        }
770eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette
771eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        /**
772504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette         * Stops the scroller at the current animation time.
773eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette         */
774504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette        public void requestStop() {
775504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette            final long currentTime = AnimationUtils.currentAnimationTimeMillis();
776504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette            mEffectiveRampDown = constrain((int) (currentTime - mStartTime), 0, mRampDownDuration);
777504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette            mStopValue = getValueAt(currentTime);
778504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette            mStopTime = currentTime;
779504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette        }
780504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette
781eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        public boolean isFinished() {
782504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette            return mStopTime > 0
783504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette                    && AnimationUtils.currentAnimationTimeMillis() > mStopTime + mEffectiveRampDown;
784504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette        }
785504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette
786504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette        private float getValueAt(long currentTime) {
787504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette            if (currentTime < mStartTime) {
788504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette                return 0f;
789504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette            } else if (mStopTime < 0 || currentTime < mStopTime) {
790504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette                final long elapsedSinceStart = currentTime - mStartTime;
791504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette                return 0.5f * constrain(elapsedSinceStart / (float) mRampUpDuration, 0, 1);
792504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette            } else {
793504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette                final long elapsedSinceEnd = currentTime - mStopTime;
794504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette                return (1 - mStopValue) + mStopValue
795504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette                        * constrain(elapsedSinceEnd / (float) mEffectiveRampDown, 0, 1);
796eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette            }
797eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        }
798eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette
799eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        /**
800504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette         * Interpolates the value along a parabolic curve corresponding to the equation
801504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette         * <code>y = -4x * (x-1)</code>.
802504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette         *
803504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette         * @param value The value to interpolate, between 0 and 1.
804504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette         * @return the interpolated value, between 0 and 1.
805eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette         */
806504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette        private float interpolateValue(float value) {
807504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette            return -4 * value * value + 4 * value;
808eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        }
809eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette
810eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        /**
811eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette         * Computes the current scroll deltas. This usually only be called after
812eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette         * starting the scroller with {@link #start()}.
813eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette         *
814eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette         * @see #getDeltaX()
815eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette         * @see #getDeltaY()
816eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette         */
817eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        public void computeScrollDelta() {
818504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette            if (mDeltaTime == 0) {
819504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette                throw new RuntimeException("Cannot compute scroll delta before calling start()");
820eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette            }
821eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette
822504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette            final long currentTime = AnimationUtils.currentAnimationTimeMillis();
823504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette            final float value = getValueAt(currentTime);
824504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette            final float scale = interpolateValue(value);
825eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette            final long elapsedSinceDelta = currentTime - mDeltaTime;
826eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette
827eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette            mDeltaTime = currentTime;
828eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette            mDeltaX = (int) (elapsedSinceDelta * scale * mTargetVelocityX);
829eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette            mDeltaY = (int) (elapsedSinceDelta * scale * mTargetVelocityY);
830eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        }
831eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette
832eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        /**
833eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette         * Sets the target velocity for this scroller.
834eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette         *
835eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette         * @param x The target X velocity in pixels per millisecond.
836eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette         * @param y The target Y velocity in pixels per millisecond.
837eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette         */
838eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        public void setTargetVelocity(float x, float y) {
839eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette            mTargetVelocityX = x;
840eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette            mTargetVelocityY = y;
841eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        }
842eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette
843504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette        public int getHorizontalDirection() {
844504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette            return (int) (mTargetVelocityX / Math.abs(mTargetVelocityX));
845504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette        }
846504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette
847504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette        public int getVerticalDirection() {
848504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette            return (int) (mTargetVelocityY / Math.abs(mTargetVelocityY));
849504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette        }
850504f6b9c6bcc6b2f50b2aaf4cf9b9bcae7e0e755Alan Viverette
851eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        /**
852eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette         * The distance traveled in the X-coordinate computed by the last call
853eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette         * to {@link #computeScrollDelta()}.
854eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette         */
855eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        public int getDeltaX() {
856eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette            return mDeltaX;
857eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        }
858eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette
859eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        /**
860eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette         * The distance traveled in the Y-coordinate computed by the last call
861eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette         * to {@link #computeScrollDelta()}.
862eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette         */
863eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        public int getDeltaY() {
864eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette            return mDeltaY;
865eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette        }
866eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette    }
867eb38a77f4c2b50da454ce0720ceb056f4932f4aeAlan Viverette}
868