165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane/*
265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * Copyright (C) 2014 The Android Open Source Project
365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane *
465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * Licensed under the Apache License, Version 2.0 (the "License");
565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * you may not use this file except in compliance with the License.
665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * You may obtain a copy of the License at
765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane *
865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane *      http://www.apache.org/licenses/LICENSE-2.0
965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane *
1065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * Unless required by applicable law or agreed to in writing, software
1165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * distributed under the License is distributed on an "AS IS" BASIS,
1265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * See the License for the specific language governing permissions and
1465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * limitations under the License.
1565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane */
1665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
1765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lanepackage com.android.tv.settings.widget;
1865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
1965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.content.Context;
2065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.content.res.Configuration;
2165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.view.View;
2265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.view.animation.DecelerateInterpolator;
2365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.view.animation.LinearInterpolator;
2465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.widget.Scroller;
2565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
2665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane/**
2765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * Maintains a Scroller object and two axis scrolling information
2865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane */
2965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lanepublic class ScrollController {
3065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    /**
3165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     * try to keep focused view kept in middle of viewport, focus move to the side of viewport when
3265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     * scroll to the beginning or end, this will make sure you won't see blank space in viewport
3365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     * {@link Axis.ItemWindow#setCount(int)} defines the size of window (how many items) we are
3465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     * trying to keep in the middle. <p>
3565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     * The middle point is calculated by "scrollCenterOffset" or "scrollCenterOffsetPercent";
3665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     * if none of these two are defined,  default value is 1/2 of the size.
3765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     *
3865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     * @see Axis#setScrollCenterStrategy(int)
3965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     * @see Axis#getSystemScrollPos(int)
4065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     */
4165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    public final static int SCROLL_CENTER_IN_MIDDLE = 0;
4265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
4365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    /**
4465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     * focus view kept at a fixed location, might see blank space. The distance of fixed location
4565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     * to left/top is given by {@link Axis#setScrollCenterOffset(int)}
4665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     *
4765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     * @see Axis#setScrollCenterStrategy(int)
4865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     * @see Axis#getSystemScrollPos(int)
4965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     */
5065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    public final static int SCROLL_CENTER_FIXED = 1;
5165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
5265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    /**
5365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     * focus view kept at a fixed percentage distance from the left/top of the view,
5465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     * might see blank space. The offset percent is set by
5565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     * {@link Axis#setScrollCenterOffsetPercent(int)}. A fixed offset from this
5665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     * position may also be set with {@link Axis#setScrollCenterOffset(int)}.
5765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     *
5865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     * @see Axis#setScrollCenterStrategy(int)
5965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     * @see Axis#getSystemScrollPos(int)
6065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     */
6165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    public final static int SCROLL_CENTER_FIXED_PERCENT = 2;
6265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
6365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    /**
6465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     * focus view kept at a fixed location, might see blank space. The distance of fixed location
6565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     * to right/bottom is given by {@link Axis#setScrollCenterOffset(int)}
6665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     *
6765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     * @see Axis#setScrollCenterStrategy(int)
6865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     * @see Axis#getSystemScrollPos(int)
6965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     */
7065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    public final static int SCROLL_CENTER_FIXED_TO_END = 3;
7165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
7265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    /**
7365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     * Align center of the item
7465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     */
7565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    public final static int SCROLL_ITEM_ALIGN_CENTER = 0;
7665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
7765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    /**
7865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     * Align left/top of the item
7965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     */
8065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    public final static int SCROLL_ITEM_ALIGN_LOW = 1;
8165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
8265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    /**
8365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     * Align right/bottom of the item
8465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     */
8565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    public final static int SCROLL_ITEM_ALIGN_HIGH = 2;
8665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
8765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    /** operation not allowed */
8865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    public final static int OPERATION_DISABLE = 0;
8965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
9065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    /**
9165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     * operation is using {@link Axis#mScrollMin} {@link Axis#mScrollMax}, see description in
9265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     * {@link Axis#mScrollCenter}
9365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     */
9465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    public final static int OPERATION_NOTOUCH = 1;
9565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
9665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    /**
9765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     * operation is using {@link Axis#mTouchScrollMax} and {@link Axis#mTouchScrollMin}, see
9865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     * description in {@link Axis#mScrollCenter}
9965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     */
10065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    public final static int OPERATION_TOUCH = 2;
10165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
10265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    /**
10365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     * maps to OPERATION_TOUCH for touchscreen, OPERATION_NORMAL for non-touchscreen
10465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     */
10565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    public final static int OPERATION_AUTO = 3;
10665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
10765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private static final int SCROLL_DURATION_MIN = 250;
10865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private static final int SCROLL_DURATION_MAX = 1500;
10965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private static final int SCROLL_DURATION_PAGE_MIN = 250;
11065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    // millisecond per pixel
11165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private static final float SCROLL_DURATION_MS_PER_PIX = 0.25f;
11265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
11365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    /**
11465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     * Maintains scroll information in one direction
11565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     */
11665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    public static class Axis {
11765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        private int mOperationMode = OPERATION_NOTOUCH;
11865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        /**
11965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane         * In {@link ScrollController#OPERATION_TOUCH} mode:<br>
12065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane         * {@link #mScrollCenter} changes from {@link #mTouchScrollMin} and
12165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane         * {@link #mTouchScrollMax}; focus won't moved to two sides when scroll to edge of view
12265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane         * port.
12365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane         * <p>
12465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane         * In {@link ScrollController#OPERATION_NOTOUCH} mode:<br>
12565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane         * mScrollCenter changes from {@link #mScrollMin} and {@link #mScrollMax}. It is different
12665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane         * than {@link View#getScrollX()} which starts from left edge of first child; mScrollCenter
12765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane         * starts from center of first child, ends at center of last child; expanded views are
12865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane         * excluded from calculating the mScrollCenter. We convert the mScrollCenter to system
12965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane         * scroll position (see {@link ScrollAdapterView#adjustSystemScrollPos}), note it's not
13065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane         * necessarily a linear transformation between system scrollX and mScrollCenter. <br>
13165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane         * For {@link #SCROLL_CENTER_IN_MIDDLE}: <br>
13265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane         * When mScrollCenter is close to {@link #mScrollMin}, {@link View#getScrollX()} will be
13365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane         * fixed 0, but mScrollCenter is still decreasing, so we can move focus from the item which
13465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane         * is at center of screen to the first child. <br>
13565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane         * For {@link #SCROLL_CENTER_FIXED} and
13665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane         * {@link #SCROLL_CENTER_FIXED_PERCENT}: It's a easy linear conversion
13765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane         * applied
13865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane         * <p>
13965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane         * mScrollCenter is also used to calculate dynamic transformation based on how far a view
14065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane         * is from the mScrollCenter. For example, the views with center close to mScrollCenter
14165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane         * will be scaled up in {@link ScrollAdapterView#applyTransformations}
14265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane         */
14365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        private float mScrollCenter;
14465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        /**
14565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane         * Maximum scroll value, initially unlimited until we will get the value when scroll to the
14665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane         * last item of ListAdapter and set the value to center of last child
14765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane         */
14865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        private int mScrollMax;
14965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        /**
15065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane         * scroll max for standard touch friendly operation, i.e. focus will not move to side when
15165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane         * scroll to two edges.
15265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane         */
15365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        private int mTouchScrollMax;
15465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        /** right/bottom edge of last child */
15565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        private int mMaxEdge;
15665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        /** left/top edge of first child, typically should be zero*/
15765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        private int mMinEdge;
15865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        /** Minimum scroll value, point to center of first child, typically half of child size */
15965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        private int mScrollMin;
16065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        /**
16165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane         * scroll min for standard touch friendly operation, i.e. focus will not move to side when
16265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane         * scroll to two edges.
16365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane         */
16465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        private int mTouchScrollMin;
16565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
16665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        private int mScrollItemAlign = SCROLL_ITEM_ALIGN_CENTER;
16765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
16865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        private boolean mSelectedTakesMoreSpace = false;
16965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
17065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        /** the offset set by a mouse dragging event */
17165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        private float mDragOffset;
17265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
17365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        /**
17465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane         * Total extra spaces.  Divided into four parts:<p>
17565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane         * 1.  extraSpace before scrollPosition, given by {@link #mExtraSpaceLow}
17665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane         *     This value is animating from the extra space of "transition from" to the value
17765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane         *     of "transition to"<p>
17865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane         * 2.  extraSpace after scrollPosition<p>
17965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane         * 3.  size of expanded view of "transition from"<p>
18065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane         * 4.  size of expanded view of "transition to"<p>
18165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane         * Among the four parts: 2,3,4 are after scroll position.<p>
18265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane         * 3,4 are included in mExpandedSize when {@link #mSelectedTakesMoreSpace} is true<p>
18365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane         * */
18465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        private int mExpandedSize;
18565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        /** extra space used before the scroll position */
18665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        private int mExtraSpaceLow;
18765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        private int mExtraSpaceHigh;
18865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
18965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        private int mAlignExtraOffset;
19065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
19165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        /**
19265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane         * Describes how to put the mScrollCenter in the view port different types affects how to
19365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane         * translate mScrollCenter to system scroll position, see details in getSystemScrollPos().
19465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane         */
19565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        private int mScrollCenterStrategy;
19665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
19765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        /**
19865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane         * used when {@link #mScrollCenterStrategy} is
19965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane         * {@link #SCROLL_CENTER_FIXED}, {@link #SCROLL_CENTER_FIXED_PERCENT} or
20065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane         * {@link #SCROLL_CENTER_FIXED_TO_END}, the offset for the fixed location of center
20165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane         * scroll position relative to left/top,  percentage or right/bottom
20265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane         */
20365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        private int mScrollCenterOffset = -1;
20465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
20565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        /**
20665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane         * used when {@link #mScrollCenterStrategy} is
20765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane         * {@link #SCROLL_CENTER_FIXED_PERCENT}. The ratio of the view's height
20865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane         * at which to place the scroll center from the top.
20965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane         */
21065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        private float mScrollCenterOffsetPercent = -1;
21165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
21265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        /** represents position information of child views, see {@link ItemWindow} */
21365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        public static class Item {
21465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
21565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            private int mIndex;
21665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            private int mLow;
21765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            private int mHigh;
21865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            private int mCenter;
21965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
22065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            public Item() {
22165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                mIndex = -1;
22265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
22365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
22465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            final public int getLow() {
22565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                return mLow;
22665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
22765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
22865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            final public int getHigh() {
22965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                return mHigh;
23065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
23165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
23265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            final public int getCenter() {
23365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                return mCenter;
23465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
23565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
23665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            final public int getIndex() {
23765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                return mIndex;
23865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
23965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
24065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            /** set low bound, high bound and index for the item */
24165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            final public void setValue(int index, int low, int high) {
24265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                mIndex = index;
24365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                mLow = low;
24465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                mHigh = high;
24565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                mCenter = (low + high) / 2;
24665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
24765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
24865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            final public boolean isValid() {
24965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                return mIndex >= 0;
25065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
25165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
25265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            @Override
25365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            final public String toString() {
25465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                return mIndex + "[" + mLow + "," + mHigh + "]";
25565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
25665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
25765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
25865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        private int mSize;
25965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
26065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        private int mPaddingLow;
26165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
26265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        private int mPaddingHigh;
26365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
26465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        private Lerper mLerper;
26565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
26665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        private String mName; // for debugging
26765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
26865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        public Axis(Lerper lerper, String name) {
26965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mScrollCenterStrategy = SCROLL_CENTER_IN_MIDDLE;
27065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mLerper = lerper;
27165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            reset();
27265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mName = name;
27365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
27465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
27565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        final public int getScrollCenterStrategy() {
27665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return mScrollCenterStrategy;
27765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
27865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
27965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        final public void setScrollCenterStrategy(int scrollCenterStrategy) {
28065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mScrollCenterStrategy = scrollCenterStrategy;
28165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
28265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
28365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        final public int getScrollCenterOffset() {
28465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return mScrollCenterOffset;
28565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
28665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
28765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        final public void setScrollCenterOffset(int scrollCenterOffset) {
28865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mScrollCenterOffset = scrollCenterOffset;
28965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
29065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
29165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        final public void setScrollCenterOffsetPercent(int scrollCenterOffsetPercent) {
29265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            if (scrollCenterOffsetPercent < 0) {
29365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                scrollCenterOffsetPercent = 0;
29465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            } else if (scrollCenterOffsetPercent > 100) {
29565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                scrollCenterOffsetPercent = 100;
29665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
29765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mScrollCenterOffsetPercent =  ( scrollCenterOffsetPercent / 100.0f);
29865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
29965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
30065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        final public void setSelectedTakesMoreSpace(boolean selectedTakesMoreSpace) {
30165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mSelectedTakesMoreSpace = selectedTakesMoreSpace;
30265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
30365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
30465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        final public boolean getSelectedTakesMoreSpace() {
30565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return mSelectedTakesMoreSpace;
30665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
30765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
30865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        final public void setScrollItemAlign(int align) {
30965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mScrollItemAlign = align;
31065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
31165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
31265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        final public int getScrollItemAlign() {
31365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return mScrollItemAlign;
31465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
31565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
31665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        final public int getScrollCenter() {
31765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return (int) mScrollCenter;
31865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
31965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
32065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        final public void setOperationMode(int mode) {
32165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mOperationMode = mode;
32265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
32365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
32465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        private int scrollMin() {
32565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return mOperationMode == OPERATION_TOUCH ? mTouchScrollMin : mScrollMin;
32665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
32765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
32865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        private int scrollMax() {
32965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return mOperationMode == OPERATION_TOUCH ? mTouchScrollMax : mScrollMax;
33065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
33165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
33265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        /** update scroll min and minEdge,  Integer.MIN_VALUE means unknown*/
33365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        final public void updateScrollMin(int scrollMin, int minEdge) {
33465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mScrollMin = scrollMin;
33565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            if (mScrollCenter < mScrollMin) {
33665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                mScrollCenter = mScrollMin;
33765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
33865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mMinEdge = minEdge;
33965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            if (mScrollCenterStrategy != SCROLL_CENTER_IN_MIDDLE
34065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    || mScrollMin == Integer.MIN_VALUE) {
34165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                mTouchScrollMin = mScrollMin;
34265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            } else {
34365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                mTouchScrollMin = Math.max(mScrollMin, mMinEdge + mSize / 2);
34465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
34565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
34665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
34765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        public void invalidateScrollMin() {
34865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mScrollMin = Integer.MIN_VALUE;
34965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mMinEdge = Integer.MIN_VALUE;
35065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mTouchScrollMin = Integer.MIN_VALUE;
35165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
35265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
35365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        /** update scroll max and maxEdge,  Integer.MAX_VALUE means unknown*/
35465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        final public void updateScrollMax(int scrollMax, int maxEdge) {
35565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mScrollMax = scrollMax;
35665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            if (mScrollCenter > mScrollMax) {
35765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                mScrollCenter = mScrollMax;
35865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
35965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mMaxEdge = maxEdge;
36065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            if (mScrollCenterStrategy != SCROLL_CENTER_IN_MIDDLE
36165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    || mScrollMax == Integer.MAX_VALUE) {
36265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                mTouchScrollMax = mScrollMax;
36365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            } else {
36465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                mTouchScrollMax = Math.min(mScrollMax, mMaxEdge - mSize / 2);
36565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
36665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
36765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
36865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        public void invalidateScrollMax() {
36965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mScrollMax = Integer.MAX_VALUE;
37065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mMaxEdge = Integer.MAX_VALUE;
37165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mTouchScrollMax = Integer.MAX_VALUE;
37265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
37365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
37465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        final public boolean canScroll(boolean forward) {
37565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            if (forward) {
37665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                if (mScrollCenter >= mScrollMax) {
37765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    return false;
37865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                }
37965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            } else {
38065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                if (mScrollCenter <= mScrollMin) {
38165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    return false;
38265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                }
38365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
38465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return true;
38565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
38665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
38765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        private boolean updateScrollCenter(float scrollTarget, boolean lerper) {
38865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mDragOffset = 0;
38965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            int scrollMin = scrollMin();
39065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            int scrollMax = scrollMax();
39165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            boolean overScroll = false;
39265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            if (scrollMin >= scrollMax) {
39365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                scrollTarget = mScrollCenter;
39465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                overScroll = true;
39565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            } else if (scrollTarget < scrollMin) {
39665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                scrollTarget = scrollMin;
39765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                overScroll = true;
39865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            } else if (scrollTarget > scrollMax) {
39965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                scrollTarget = scrollMax;
40065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                overScroll = true;
40165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
40265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            if (lerper) {
40365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                mScrollCenter = mLerper.getValue(mScrollCenter, scrollTarget);
40465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            } else {
40565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                mScrollCenter = scrollTarget;
40665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
40765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return overScroll;
40865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
40965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
41065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        private void updateFromDrag() {
41165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            updateScrollCenter(mScrollCenter + mDragOffset, false);
41265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
41365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
41465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        private void dragBy(float distanceX) {
41565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mDragOffset += distanceX;
41665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
41765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
41865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        private void reset() {
41965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mScrollCenter = Integer.MIN_VALUE;
42065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mScrollMin = Integer.MIN_VALUE;
42165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mMinEdge = Integer.MIN_VALUE;
42265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mTouchScrollMin = Integer.MIN_VALUE;
42365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mScrollMax = Integer.MAX_VALUE;
42465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mMaxEdge = Integer.MAX_VALUE;
42565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mTouchScrollMax = Integer.MAX_VALUE;
42665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mExpandedSize = 0;
42765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mDragOffset = 0;
42865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
42965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
43065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        final public boolean isMinUnknown() {
43165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return mScrollMin == Integer.MIN_VALUE;
43265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
43365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
43465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        final public boolean isMaxUnknown() {
43565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return mScrollMax == Integer.MAX_VALUE;
43665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
43765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
43865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        final public int getSizeForExpandableItem() {
43965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return mSize - mPaddingLow - mPaddingHigh - mExpandedSize;
44065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
44165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
44265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        final public void setSize(int size) {
44365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mSize = size;
44465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
44565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
44665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        final public void setExpandedSize(int expandedSize) {
44765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mExpandedSize = expandedSize;
44865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
44965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
45065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        final public void setExtraSpaceLow(int extraSpaceLow) {
45165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mExtraSpaceLow = extraSpaceLow;
45265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
45365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
45465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        final public void setExtraSpaceHigh(int extraSpaceHigh) {
45565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mExtraSpaceHigh = extraSpaceHigh;
45665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
45765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
45865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        final public void setAlignExtraOffset(int extraOffset) {
45965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mAlignExtraOffset = extraOffset;
46065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
46165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
46265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        final public void setPadding(int paddingLow, int paddingHigh) {
46365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mPaddingLow = paddingLow;
46465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mPaddingHigh = paddingHigh;
46565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
46665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
46765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        final public int getPaddingLow() {
46865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return mPaddingLow;
46965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
47065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
47165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        final public int getPaddingHigh() {
47265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return mPaddingHigh;
47365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
47465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
47565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        final public int getSystemScrollPos() {
47665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return getSystemScrollPos((int) mScrollCenter);
47765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
47865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
47965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        final public int getSystemScrollPos(int scrollCenter) {
48065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            scrollCenter += mAlignExtraOffset;
48165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
48265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            // For the "FIXED" strategy family:
48365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            int compensate = mSelectedTakesMoreSpace ? mExtraSpaceLow : -mExtraSpaceLow;
48465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            if (mScrollCenterStrategy == SCROLL_CENTER_FIXED) {
48565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                return scrollCenter - mScrollCenterOffset + compensate;
48665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            } else if (mScrollCenterStrategy == SCROLL_CENTER_FIXED_TO_END) {
48765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                return scrollCenter - (mSize - mScrollCenterOffset) + compensate;
48865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            } else if (mScrollCenterStrategy == SCROLL_CENTER_FIXED_PERCENT) {
48965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                return (int) (scrollCenter - mScrollCenterOffset - mSize
49065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                        * mScrollCenterOffsetPercent) + compensate;
49165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
49265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            int clientSize = mSize - mPaddingLow - mPaddingHigh;
49365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            // For SCROLL_CENTER_IN_MIDDLE, first calculate the middle point:
49465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            // if the scrollCenterOffset or scrollCenterOffsetPercent is specified,
49565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            // use it for middle point,  otherwise, use 1/2 of the size
49665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            int middlePosition;
49765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            if (mScrollCenterOffset >= 0) {
49865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                middlePosition = mScrollCenterOffset - mPaddingLow;
49965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            } else if (mScrollCenterOffsetPercent >= 0) {
50065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                middlePosition = (int) (mSize * mScrollCenterOffsetPercent) - mPaddingLow;
50165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            } else {
50265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                middlePosition = clientSize / 2;
50365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
50465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            int afterMiddlePosition = clientSize - middlePosition;
50565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            // Following code for mSelectedTakesMoreSpace = true/false is quite similar,
50665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            // but it's still more clear and easier to debug when separating them.
50765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            boolean isMinUnknown = isMinUnknown();
50865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            boolean isMaxUnknown = isMaxUnknown();
50965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            if (mSelectedTakesMoreSpace) {
51065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                int extraSpaceLow;
51165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                switch (getScrollItemAlign()) {
51265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    case SCROLL_ITEM_ALIGN_LOW:
51365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                        extraSpaceLow = 0;
51465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                        break;
51565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    case SCROLL_ITEM_ALIGN_HIGH:
51665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                        extraSpaceLow = mExtraSpaceLow + mExtraSpaceHigh;
51765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                        break;
51865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    case SCROLL_ITEM_ALIGN_CENTER:
51965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    default:
52065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                        extraSpaceLow = mExtraSpaceLow;
52165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                        break;
52265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                }
52365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                if (!isMinUnknown && !isMaxUnknown) {
52465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    if (mMaxEdge - mMinEdge + mExpandedSize <= clientSize) {
52565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                        // total children size is less than view port: align the left edge
52665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                        // of first child to view port's left edge
52765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                        return mMinEdge - mPaddingLow;
52865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    }
52965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                }
53065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                if (!isMinUnknown) {
53165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    if (scrollCenter - mMinEdge + extraSpaceLow <= middlePosition) {
53265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                        // scroll center is within half of view port size: align the left edge
53365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                        // of first child to the left edge of view port
53465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                        return mMinEdge - mPaddingLow;
53565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    }
53665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                }
53765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                if (!isMaxUnknown) {
53865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    int spaceAfterScrollCenter = mExpandedSize - extraSpaceLow;
53965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    if (mMaxEdge - scrollCenter + spaceAfterScrollCenter <= afterMiddlePosition) {
54065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                        // scroll center is very close to the right edge of view port : align the
54165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                        // right edge of last children (plus expanded size) to view port's right
54265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                        return mMaxEdge -mPaddingLow - (clientSize - mExpandedSize );
54365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    }
54465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                }
54565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                // else put scroll center in middle of view port
54665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                return scrollCenter - middlePosition - mPaddingLow + extraSpaceLow;
54765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            } else {
54865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                int shift;
54965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                switch (getScrollItemAlign()) {
55065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    case SCROLL_ITEM_ALIGN_LOW:
55165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                        shift = - mExtraSpaceLow;
55265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                        break;
55365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    case SCROLL_ITEM_ALIGN_HIGH:
55465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                        shift = + mExtraSpaceHigh;
55565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                        break;
55665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    case SCROLL_ITEM_ALIGN_CENTER:
55765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    default:
55865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                        shift = 0;
55965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                        break;
56065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                }
56165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                if (!isMinUnknown && !isMaxUnknown) {
56265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    if (mMaxEdge - mMinEdge + mExpandedSize <= clientSize) {
56365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                        // total children size is less than view port: align the left edge
56465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                        // of first child to view port's left edge
56565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                        return mMinEdge - mPaddingLow;
56665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    }
56765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                }
56865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                if (!isMinUnknown) {
56965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    if (scrollCenter + shift - mMinEdge <= middlePosition) {
57065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                        // scroll center is within half of view port size: align the left edge
57165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                        // of first child to the left edge of view port
57265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                        return mMinEdge - mPaddingLow;
57365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    }
57465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                }
57565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                if (!isMaxUnknown) {
57665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    if (mMaxEdge - scrollCenter - shift + mExpandedSize <= afterMiddlePosition) {
57765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                        // scroll center is very close to the right edge of view port : align the
57865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                        // right edge of last children (plus expanded size) to view port's right
57965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                        return mMaxEdge -mPaddingLow - (clientSize - mExpandedSize );
58065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    }
58165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                }
58265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                // else put scroll center in middle of view port
58365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                return scrollCenter - middlePosition - mPaddingLow + shift;
58465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
58565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
58665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
58765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        @Override
58865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        public String toString() {
58965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return "center: " + mScrollCenter + " min:" + mMinEdge + "," + mScrollMin +
59065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    " max:" + mScrollMax + "," + mMaxEdge;
59165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
59265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
59365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
59465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
59565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private Context mContext;
59665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
59765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    // we separate Scrollers for scroll animation and fling animation; this is because we want a
59865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    // flywheel feature for fling animation, ScrollAdapterView inserts scroll animation between
59965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    // fling animations, the fling animation will mistakenly continue the old velocity of scroll
60065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    // animation: that's wrong, we want fling animation pickup the old velocity of last fling.
60165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private Scroller mScrollScroller;
60265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private Scroller mFlingScroller;
60365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
60465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private final static int STATE_NONE = 0;
60565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
60665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    /** using fling scroller */
60765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private final static int STATE_FLING = 1;
60865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
60965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    /** using scroll scroller */
61065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private final static int STATE_SCROLL = 2;
61165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
61265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    /** using drag */
61365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private final static int STATE_DRAG = 3;
61465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
61565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private int mState = STATE_NONE;
61665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
61765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private int mOrientation = ScrollAdapterView.HORIZONTAL;
61865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
61965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private Lerper mLerper = new Lerper();
62065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
62165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    final public Axis vertical = new Axis(mLerper, "vertical");
62265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
62365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    final public Axis horizontal = new Axis(mLerper, "horizontal");
62465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
62565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private Axis mMainAxis = horizontal;
62665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
62765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private Axis mSecondAxis = vertical;
62865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
62965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    /** fling operation mode */
63065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private int mFlingMode = OPERATION_AUTO;
63165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
63265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    /** drag operation mode */
63365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private int mDragMode = OPERATION_AUTO;
63465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
63565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    /** scroll operation mode (for DPAD) */
63665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private int mScrollMode = OPERATION_NOTOUCH;
63765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
63865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    /** the major movement is in horizontal or vertical */
63965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private boolean mMainHorizontal;
64065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private boolean mHorizontalForward = true;
64165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private boolean mVerticalForward = true;
64265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
64365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    final public Lerper lerper() {
64465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        return mLerper;
64565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
64665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
64765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    final public Axis mainAxis() {
64865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        return mMainAxis;
64965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
65065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
65165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    final public Axis secondAxis() {
65265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        return mSecondAxis;
65365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
65465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
65565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    final public void setLerperDivisor(float divisor) {
65665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        mLerper.setDivisor(divisor);
65765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
65865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
65965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    public ScrollController(Context context) {
66065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        mContext = context;
66165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        // Quint easeOut
66265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        mScrollScroller = new Scroller(mContext, new DecelerateInterpolator(2));
66365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        mFlingScroller = new Scroller(mContext, new LinearInterpolator());
66465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
66565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
66665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    final public void setOrientation(int orientation) {
66765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        int align = mainAxis().getScrollItemAlign();
66865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        boolean selectedTakesMoreSpace = mainAxis().getSelectedTakesMoreSpace();
66965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        mOrientation = orientation;
67065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        if (mOrientation == ScrollAdapterView.HORIZONTAL) {
67165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mMainAxis = horizontal;
67265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mSecondAxis = vertical;
67365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        } else {
67465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mMainAxis = vertical;
67565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mSecondAxis = horizontal;
67665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
67765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        mMainAxis.setScrollItemAlign(align);
67865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        mSecondAxis.setScrollItemAlign(SCROLL_ITEM_ALIGN_CENTER);
67965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        mMainAxis.setSelectedTakesMoreSpace(selectedTakesMoreSpace);
68065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        mSecondAxis.setSelectedTakesMoreSpace(false);
68165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
68265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
68365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    public void setScrollItemAlign(int align) {
68465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        mainAxis().setScrollItemAlign(align);
68565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
68665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
68765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    public int getScrollItemAlign() {
68865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        return mainAxis().getScrollItemAlign();
68965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
69065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
69165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    final public int getOrientation() {
69265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        return mOrientation;
69365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
69465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
69565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    final public int getFlingMode() {
69665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        return mFlingMode;
69765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
69865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
69965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    final public void setFlingMode(int mode) {
70065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        this.mFlingMode = mode;
70165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
70265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
70365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    final public int getDragMode() {
70465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        return mDragMode;
70565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
70665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
70765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    final public void setDragMode(int mode) {
70865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        this.mDragMode = mode;
70965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
71065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
71165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    final public int getScrollMode() {
71265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        return mScrollMode;
71365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
71465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
71565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    final public void setScrollMode(int mode) {
71665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        this.mScrollMode = mode;
71765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
71865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
71965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    final public float getCurrVelocity() {
72065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        if (mState == STATE_FLING) {
72165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return mFlingScroller.getCurrVelocity();
72265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        } else if (mState == STATE_SCROLL) {
72365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return mScrollScroller.getCurrVelocity();
72465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
72565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        return 0;
72665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
72765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
72865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    final public boolean canScroll(int dx, int dy) {
72965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        if (dx == 0 && dy == 0) {
73065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return false;
73165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
73265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        return (dx == 0 || horizontal.canScroll(dx < 0)) &&
73365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                (dy == 0 || vertical.canScroll(dy < 0));
73465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
73565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
73665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private int getMode(int mode) {
73765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        if (mode == OPERATION_AUTO) {
73865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            if (mContext.getResources().getConfiguration().touchscreen
73965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    == Configuration.TOUCHSCREEN_NOTOUCH) {
74065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                mode = OPERATION_NOTOUCH;
74165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            } else {
74265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                mode = OPERATION_TOUCH;
74365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
74465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
74565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        return mode;
74665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
74765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
74865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private void updateDirection(float dx, float dy) {
74965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        mMainHorizontal = Math.abs(dx) >= Math.abs(dy);
75065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        if (dx > 0) {
75165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mHorizontalForward = true;
75265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        } else if (dx < 0) {
75365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mHorizontalForward = false;
75465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
75565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        if (dy > 0) {
75665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mVerticalForward = true;
75765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        } else if (dy < 0) {
75865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mVerticalForward = false;
75965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
76065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
76165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
76265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    final public boolean fling(int velocity_x, int velocity_y){
76365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        if (mFlingMode == OPERATION_DISABLE) {
76465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return false;
76565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
76665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        final int operationMode = getMode(mFlingMode);
76765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        horizontal.setOperationMode(operationMode);
76865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        vertical.setOperationMode(operationMode);
76965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        mState = STATE_FLING;
77065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        mFlingScroller.fling((int)(horizontal.mScrollCenter),
77165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                (int)(vertical.mScrollCenter),
77265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                velocity_x,
77365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                velocity_y,
77465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                Integer.MIN_VALUE,
77565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                Integer.MAX_VALUE,
77665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                Integer.MIN_VALUE,
77765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                Integer.MAX_VALUE);
77865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        updateDirection(velocity_x, velocity_y);
77965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        return true;
78065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
78165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
78265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    final public void startScroll(int dx, int dy, boolean easeFling, int duration, boolean page) {
78365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        if (mScrollMode == OPERATION_DISABLE) {
78465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return;
78565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
78665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        final int operationMode = getMode(mScrollMode);
78765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        horizontal.setOperationMode(operationMode);
78865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        vertical.setOperationMode(operationMode);
78965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        Scroller scroller;
79065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        if (easeFling) {
79165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mState = STATE_FLING;
79265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            scroller = mFlingScroller;
79365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        } else {
79465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mState = STATE_SCROLL;
79565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            scroller = mScrollScroller;
79665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
79765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        int basex = horizontal.getScrollCenter();
79865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        int basey = vertical.getScrollCenter();
79965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        if (!scroller.isFinished()) {
80065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            // during scrolling, we should continue from getCurrX/getCurrY() (might be different
80165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            // than current Scroll Center due to Lerper)
80265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            dx = basex + dx - scroller.getCurrX();
80365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            dy = basey + dy - scroller.getCurrY();
80465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            basex = scroller.getCurrX();
80565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            basey = scroller.getCurrY();
80665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
80765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        updateDirection(dx, dy);
80865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        if (easeFling) {
80965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            float curDx = Math.abs(mFlingScroller.getFinalX() - mFlingScroller.getStartX());
81065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            float curDy = Math.abs(mFlingScroller.getFinalY() - mFlingScroller.getStartY());
81165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            float hyp = (float) Math.sqrt(curDx * curDx + curDy * curDy);
81265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            float velocity = mFlingScroller.getCurrVelocity();
81365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            float velocityX = velocity * curDx / hyp;
81465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            float velocityY = velocity * curDy / hyp;
81565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            int durationX = velocityX ==0 ? 0 : (int)((Math.abs(dx) * 1000) / velocityX);
81665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            int durationY = velocityY ==0 ? 0 : (int)((Math.abs(dy) * 1000) / velocityY);
81765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            if (duration == 0) duration = Math.max(durationX, durationY);
81865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        } else {
81965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            if (duration == 0) {
82065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                duration = getScrollDuration((int) Math.sqrt(dx * dx + dy * dy), page);
82165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
82265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
82365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        scroller.startScroll(basex, basey, dx, dy, duration);
82465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
82565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
82665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    final public int getCurrentAnimationDuration() {
82765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        Scroller scroller;
82865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        if (mState == STATE_FLING) {
82965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            scroller = mFlingScroller;
83065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        } else if (mState == STATE_SCROLL) {
83165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            scroller = mScrollScroller;
83265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        } else {
83365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return 0;
83465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
83565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        return scroller.getDuration();
83665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
83765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
83865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    final public void startScrollByMain(int deltaMain, int deltaSecond, boolean easeFling,
83965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            int duration, boolean page) {
84065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        int dx, dy;
84165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        if (mOrientation == ScrollAdapterView.HORIZONTAL) {
84265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            dx = deltaMain;
84365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            dy = deltaSecond;
84465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        } else {
84565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            dx = deltaSecond;
84665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            dy = deltaMain;
84765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
84865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        startScroll(dx, dy, easeFling, duration, page);
84965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
85065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
85165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    final public boolean dragBy(float distanceX, float distanceY) {
85265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        if (mDragMode == OPERATION_DISABLE) {
85365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return false;
85465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
85565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        final int operationMode = getMode(mDragMode);
85665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        horizontal.setOperationMode(operationMode);
85765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        vertical.setOperationMode(operationMode);
85865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        horizontal.dragBy(distanceX);
85965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        vertical.dragBy(distanceY);
86065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        mState = STATE_DRAG;
86165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        return true;
86265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
86365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
86465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    final public void stopDrag() {
86565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        mState = STATE_NONE;
86665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        vertical.mDragOffset = 0;
86765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        horizontal.mDragOffset = 0;
86865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
86965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
87065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    final public void setScrollCenterByMain(int centerMain, int centerSecond) {
87165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        if (mOrientation == ScrollAdapterView.HORIZONTAL) {
87265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            setScrollCenter(centerMain, centerSecond);
87365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        } else {
87465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            setScrollCenter(centerSecond, centerMain);
87565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
87665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
87765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
87865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    final public void setScrollCenter(int centerX, int centerY) {
87965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        horizontal.updateScrollCenter(centerX, false);
88065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        vertical.updateScrollCenter(centerY, false);
88165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        // centerX, centerY might be clipped by min/max
88265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        centerX = horizontal.getScrollCenter();
88365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        centerY = vertical.getScrollCenter();
88465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        mFlingScroller.setFinalX(centerX);
88565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        mFlingScroller.setFinalY(centerY);
88665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        mFlingScroller.abortAnimation();
88765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        mScrollScroller.setFinalX(centerX);
88865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        mScrollScroller.setFinalY(centerY);
88965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        mScrollScroller.abortAnimation();
89065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
89165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
89265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    final public int getFinalX() {
89365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        if (mState == STATE_FLING) {
89465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return mFlingScroller.getFinalX();
89565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        } else if (mState == STATE_SCROLL) {
89665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return mScrollScroller.getFinalX();
89765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
89865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        return horizontal.getScrollCenter();
89965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
90065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
90165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    final public int getFinalY() {
90265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        if (mState == STATE_FLING) {
90365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return mFlingScroller.getFinalY();
90465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        } else if (mState == STATE_SCROLL) {
90565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return mScrollScroller.getFinalY();
90665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
90765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        return vertical.getScrollCenter();
90865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
90965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
91065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    final public void setFinalX(int finalX) {
91165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        if (mState == STATE_FLING) {
91265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mFlingScroller.setFinalX(finalX);
91365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        } else if (mState == STATE_SCROLL) {
91465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mScrollScroller.setFinalX(finalX);
91565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
91665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
91765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
91865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    final public void setFinalY(int finalY) {
91965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        if (mState == STATE_FLING) {
92065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mFlingScroller.setFinalY(finalY);
92165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        } else if (mState == STATE_SCROLL) {
92265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mScrollScroller.setFinalY(finalY);
92365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
92465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
92565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
92665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    /** return true if scroll/fling animation or lerper is not stopped */
92765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    final public boolean isFinished() {
92865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        Scroller scroller;
92965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        if (mState == STATE_FLING) {
93065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            scroller = mFlingScroller;
93165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        } else if (mState == STATE_SCROLL) {
93265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            scroller = mScrollScroller;
93365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        } else if (mState == STATE_DRAG){
93465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return false;
93565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        } else {
93665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return true;
93765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
93865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        if (scroller.isFinished()) {
93965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return (horizontal.getScrollCenter() == scroller.getCurrX() &&
94065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    vertical.getScrollCenter() == scroller.getCurrY());
94165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
94265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        return false;
94365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
94465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
94565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    final public boolean isMainAxisMovingForward() {
94665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        return mOrientation == ScrollAdapterView.HORIZONTAL ?
94765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                mHorizontalForward : mVerticalForward;
94865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
94965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
95065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    final public boolean isSecondAxisMovingForward() {
95165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        return mOrientation == ScrollAdapterView.HORIZONTAL ?
95265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                mVerticalForward : mHorizontalForward;
95365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
95465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
95565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    final public int getLastDirection() {
95665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        if (mMainHorizontal) {
95765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return mHorizontalForward ? View.FOCUS_RIGHT : View.FOCUS_LEFT;
95865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        } else {
95965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return mVerticalForward ? View.FOCUS_DOWN : View.FOCUS_UP;
96065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
96165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
96265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
96365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    /**
96465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     * update scroller position, this is either trigger by fling()/startScroll() on the
96565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     * scroller object,  or lerper, or can be caused by a dragBy()
96665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     */
96765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    final public void computeAndSetScrollPosition() {
96865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        Scroller scroller;
96965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        if (mState == STATE_FLING) {
97065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            scroller = mFlingScroller;
97165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        } else if (mState == STATE_SCROLL) {
97265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            scroller = mScrollScroller;
97365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        } else if (mState == STATE_DRAG) {
97465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            if (horizontal.mDragOffset != 0 || vertical.mDragOffset !=0 ) {
97565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                horizontal.updateFromDrag();
97665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                vertical.updateFromDrag();
97765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
97865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return;
97965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        } else {
98065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return;
98165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
98265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        if (!isFinished()) {
98365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            scroller.computeScrollOffset();
98465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            horizontal.updateScrollCenter(scroller.getCurrX(), true);
98565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            vertical.updateScrollCenter(scroller.getCurrY(), true);
98665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
98765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
98865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
98965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    /** get Scroll animation duration in ms for given pixels */
99065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    final public int getScrollDuration(int distance, boolean isPage) {
99165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        int ms = (int)(distance * SCROLL_DURATION_MS_PER_PIX);
99265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        int minValue = isPage ? SCROLL_DURATION_PAGE_MIN : SCROLL_DURATION_MIN;
99365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        if (ms < minValue) {
99465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            ms = minValue;
99565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        } else if (ms > SCROLL_DURATION_MAX) {
99665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            ms = SCROLL_DURATION_MAX;
99765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
99865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        return ms;
99965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
100065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
100165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    final public void reset() {
100265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        mainAxis().reset();
100365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
100465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
100565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    @Override
100665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    public String toString() {
100765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        return new StringBuffer().append("horizontal=")
100865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                .append(horizontal.toString())
100965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                .append("vertical=")
101065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                .append(vertical.toString())
101165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                .toString();
101265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
101365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
101465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane}
1015