NestedScrollView.java revision e2104f4b5c8e3ad63570306a25e61502dfe4c418
11fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell/*
21fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * Copyright (C) 2015 The Android Open Source Project
31fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell *
41fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * Licensed under the Apache License, Version 2.0 (the "License");
51fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * you may not use this file except in compliance with the License.
61fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * You may obtain a copy of the License at
71fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell *
81fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell *      http://www.apache.org/licenses/LICENSE-2.0
91fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell *
101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * Unless required by applicable law or agreed to in writing, software
111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * distributed under the License is distributed on an "AS IS" BASIS,
121fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
131fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * See the License for the specific language governing permissions and
141fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * limitations under the License.
151fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */
161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
171fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
181fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellpackage android.support.v4.widget;
191fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
208e10080c914d1ad0784394fa3026b85535535847Aurimas Liutikasimport static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
218e10080c914d1ad0784394fa3026b85535535847Aurimas Liutikas
221fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.content.Context;
231fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.content.res.TypedArray;
241fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.graphics.Canvas;
251fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.graphics.Rect;
261fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.os.Bundle;
271fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.os.Parcel;
281fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.os.Parcelable;
29c39d9c75590eca86a5e7e32a8824ba04a0d42e9bAlan Viveretteimport android.support.annotation.RestrictTo;
301fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.support.v4.view.AccessibilityDelegateCompat;
311fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.support.v4.view.InputDeviceCompat;
321fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.support.v4.view.NestedScrollingChild;
331fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.support.v4.view.NestedScrollingChildHelper;
341fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.support.v4.view.NestedScrollingParent;
351fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.support.v4.view.NestedScrollingParentHelper;
36c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Arakiimport android.support.v4.view.ScrollingView;
371fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.support.v4.view.ViewCompat;
381fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.support.v4.view.accessibility.AccessibilityEventCompat;
391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.support.v4.view.accessibility.AccessibilityRecordCompat;
411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.util.AttributeSet;
421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.util.Log;
431fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.util.TypedValue;
441fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.view.FocusFinder;
451fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.view.KeyEvent;
461fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.view.MotionEvent;
471fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.view.VelocityTracker;
481fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.view.View;
491fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.view.ViewConfiguration;
501fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.view.ViewGroup;
511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.view.ViewParent;
521fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.view.accessibility.AccessibilityEvent;
531fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.view.animation.AnimationUtils;
54506695b4cf6b22d70d2e3647d94551e721ee0430Aurimas Liutikasimport android.widget.EdgeEffect;
551fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.widget.FrameLayout;
563035be16658d7652fdc472f971c81d8f7ffb60fdAurimas Liutikasimport android.widget.OverScroller;
571fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.widget.ScrollView;
581fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport java.util.List;
601fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
611fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell/**
621fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * NestedScrollView is just like {@link android.widget.ScrollView}, but it supports acting
631fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * as both a nested scrolling parent and child on both new and old versions of Android.
641fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * Nested scrolling is enabled by default.
651fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */
661fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellpublic class NestedScrollView extends FrameLayout implements NestedScrollingParent,
67c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki        NestedScrollingChild, ScrollingView {
681fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    static final int ANIMATED_SCROLL_GAP = 250;
691fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
701fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    static final float MAX_SCROLL_FACTOR = 0.5f;
711fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
721fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private static final String TAG = "NestedScrollView";
731fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
74cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes    /**
75cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes     * Interface definition for a callback to be invoked when the scroll
76cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes     * X or Y positions of a view change.
77cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes     *
78cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes     * <p>This version of the interface works on all versions of Android, back to API v4.</p>
79cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes     *
80cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes     * @see #setOnScrollChangeListener(OnScrollChangeListener)
81cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes     */
82cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes    public interface OnScrollChangeListener {
83cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes        /**
84cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes         * Called when the scroll position of a view changes.
85cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes         *
86cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes         * @param v The view whose scroll position has changed.
87cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes         * @param scrollX Current horizontal scroll origin.
88cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes         * @param scrollY Current vertical scroll origin.
89cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes         * @param oldScrollX Previous horizontal scroll origin.
90cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes         * @param oldScrollY Previous vertical scroll origin.
91cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes         */
92cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes        void onScrollChange(NestedScrollView v, int scrollX, int scrollY,
93cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes                int oldScrollX, int oldScrollY);
94cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes    }
95cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes
961fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private long mLastScroll;
971fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
981fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private final Rect mTempRect = new Rect();
993035be16658d7652fdc472f971c81d8f7ffb60fdAurimas Liutikas    private OverScroller mScroller;
100506695b4cf6b22d70d2e3647d94551e721ee0430Aurimas Liutikas    private EdgeEffect mEdgeGlowTop;
101506695b4cf6b22d70d2e3647d94551e721ee0430Aurimas Liutikas    private EdgeEffect mEdgeGlowBottom;
1021fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
1031fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
1041fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * Position of the last motion event.
1051fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
1061fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private int mLastMotionY;
1071fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
1081fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
1091fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * True when the layout has changed but the traversal has not come through yet.
1101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * Ideally the view hierarchy would keep track of this for us.
1111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
1121fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private boolean mIsLayoutDirty = true;
1131fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private boolean mIsLaidOut = false;
1141fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
1151fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
1161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * The child to give focus to in the event that a child has requested focus while the
1171fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * layout is dirty. This prevents the scroll from being wrong if the child has not been
1181fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * laid out before requesting focus.
1191fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
1201fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private View mChildToScrollTo = null;
1211fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
1221fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
1231fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * True if the user is currently dragging this ScrollView around. This is
1241fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * not the same as 'is being flinged', which can be checked by
1251fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * mScroller.isFinished() (flinging begins when the user lifts his finger).
1261fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
1271fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private boolean mIsBeingDragged = false;
1281fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
1291fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
1301fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * Determines speed during touch scrolling
1311fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
1321fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private VelocityTracker mVelocityTracker;
1331fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
1341fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
1351fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * When set to true, the scroll view measure its child to make it fill the currently
1361fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * visible area.
1371fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
1381fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private boolean mFillViewport;
1391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
1401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
1411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * Whether arrow scrolling is animated.
1421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
1431fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private boolean mSmoothScrollingEnabled = true;
1441fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
1451fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private int mTouchSlop;
1461fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private int mMinimumVelocity;
1471fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private int mMaximumVelocity;
1481fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
1491fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
1501fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * ID of the active pointer. This is used to retain consistency during
1511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * drags/flings if multiple pointers are used.
1521fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
1531fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private int mActivePointerId = INVALID_POINTER;
1541fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
1551fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
1561fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * Used during scrolling to retrieve the new offset within the window.
1571fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
1581fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private final int[] mScrollOffset = new int[2];
1591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private final int[] mScrollConsumed = new int[2];
1601fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private int mNestedYOffset;
1611fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
1621fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
1631fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * Sentinel value for no current active pointer.
1641fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * Used by {@link #mActivePointerId}.
1651fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
1661fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private static final int INVALID_POINTER = -1;
1671fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
1681fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private SavedState mSavedState;
1691fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
1701fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private static final AccessibilityDelegate ACCESSIBILITY_DELEGATE = new AccessibilityDelegate();
1711fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
1721fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private static final int[] SCROLLVIEW_STYLEABLE = new int[] {
1731fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            android.R.attr.fillViewport
1741fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    };
1751fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
1761fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private final NestedScrollingParentHelper mParentHelper;
1771fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private final NestedScrollingChildHelper mChildHelper;
1781fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
1791fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private float mVerticalScrollFactor;
1801fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
181cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes    private OnScrollChangeListener mOnScrollChangeListener;
182cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes
1831fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public NestedScrollView(Context context) {
1841fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        this(context, null);
1851fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
1861fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
1871fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public NestedScrollView(Context context, AttributeSet attrs) {
1881fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        this(context, attrs, 0);
1891fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
1901fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
1911fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public NestedScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
1921fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        super(context, attrs, defStyleAttr);
1931fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        initScrollView();
1941fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
1951fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        final TypedArray a = context.obtainStyledAttributes(
1961fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                attrs, SCROLLVIEW_STYLEABLE, defStyleAttr, 0);
1971fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
1981fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        setFillViewport(a.getBoolean(0, false));
1991fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
2001fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        a.recycle();
2011fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
2021fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        mParentHelper = new NestedScrollingParentHelper(this);
2031fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        mChildHelper = new NestedScrollingChildHelper(this);
2041fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
2051fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        // ...because why else would you be using this widget?
2061fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        setNestedScrollingEnabled(true);
2071fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
2081fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        ViewCompat.setAccessibilityDelegate(this, ACCESSIBILITY_DELEGATE);
2091fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
2101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
2111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    // NestedScrollingChild
2121fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
2131fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
2141fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public void setNestedScrollingEnabled(boolean enabled) {
2151fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        mChildHelper.setNestedScrollingEnabled(enabled);
2161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
2171fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
2181fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
2191fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public boolean isNestedScrollingEnabled() {
2201fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return mChildHelper.isNestedScrollingEnabled();
2211fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
2221fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
2231fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
2241fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public boolean startNestedScroll(int axes) {
2251fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return mChildHelper.startNestedScroll(axes);
2261fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
2271fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
2281fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
2291fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public void stopNestedScroll() {
2301fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        mChildHelper.stopNestedScroll();
2311fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
2321fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
2331fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
2341fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public boolean hasNestedScrollingParent() {
2351fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return mChildHelper.hasNestedScrollingParent();
2361fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
2371fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
2381fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
2391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed,
2401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            int dyUnconsumed, int[] offsetInWindow) {
2411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return mChildHelper.dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed,
2421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                offsetInWindow);
2431fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
2441fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
2451fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
2461fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) {
2471fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return mChildHelper.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow);
2481fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
2491fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
2501fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
2511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) {
2521fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return mChildHelper.dispatchNestedFling(velocityX, velocityY, consumed);
2531fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
2541fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
2551fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
2561fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public boolean dispatchNestedPreFling(float velocityX, float velocityY) {
2571fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return mChildHelper.dispatchNestedPreFling(velocityX, velocityY);
2581fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
2591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
2601fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    // NestedScrollingParent
2611fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
2621fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
2631fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) {
2641fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0;
2651fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
2661fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
2671fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
2681fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public void onNestedScrollAccepted(View child, View target, int nestedScrollAxes) {
2691fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        mParentHelper.onNestedScrollAccepted(child, target, nestedScrollAxes);
2701fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL);
2711fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
2721fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
2731fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
2741fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public void onStopNestedScroll(View target) {
275cb3b6124a87f9116ce92fcf17f59287445c41442Chris Banes        mParentHelper.onStopNestedScroll(target);
2761fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        stopNestedScroll();
2771fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
2781fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
2791fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
2801fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed,
2811fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            int dyUnconsumed) {
2821fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        final int oldScrollY = getScrollY();
2831fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        scrollBy(0, dyUnconsumed);
2841fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        final int myConsumed = getScrollY() - oldScrollY;
2851fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        final int myUnconsumed = dyUnconsumed - myConsumed;
2861fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        dispatchNestedScroll(0, myConsumed, 0, myUnconsumed, null);
2871fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
2881fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
2891fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
2901fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) {
291d370f75e50bf31d348d4e424ba8477ac77635f70Chris Banes        dispatchNestedPreScroll(dx, dy, consumed, null);
2921fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
2931fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
2941fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
2951fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) {
2961fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (!consumed) {
2971fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            flingWithNestedDispatch((int) velocityY);
2981fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            return true;
2991fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
3001fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return false;
3011fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
3021fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
3031fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
3041fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public boolean onNestedPreFling(View target, float velocityX, float velocityY) {
305d370f75e50bf31d348d4e424ba8477ac77635f70Chris Banes        return dispatchNestedPreFling(velocityX, velocityY);
3061fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
3071fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
3081fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
3091fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public int getNestedScrollAxes() {
3101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return mParentHelper.getNestedScrollAxes();
3111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
3121fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
3131fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    // ScrollView import
3141fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
315e2104f4b5c8e3ad63570306a25e61502dfe4c418Aurimas Liutikas    @Override
3161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public boolean shouldDelayChildPressedState() {
3171fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return true;
3181fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
3191fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
3201fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
3211fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    protected float getTopFadingEdgeStrength() {
3221fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (getChildCount() == 0) {
3231fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            return 0.0f;
3241fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
3251fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
3261fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        final int length = getVerticalFadingEdgeLength();
3271fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        final int scrollY = getScrollY();
3281fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (scrollY < length) {
3291fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            return scrollY / (float) length;
3301fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
3311fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
3321fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return 1.0f;
3331fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
3341fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
3351fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
3361fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    protected float getBottomFadingEdgeStrength() {
3371fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (getChildCount() == 0) {
3381fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            return 0.0f;
3391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
3401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
3411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        final int length = getVerticalFadingEdgeLength();
3421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        final int bottomEdge = getHeight() - getPaddingBottom();
3431fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        final int span = getChildAt(0).getBottom() - getScrollY() - bottomEdge;
3441fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (span < length) {
3451fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            return span / (float) length;
3461fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
3471fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
3481fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return 1.0f;
3491fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
3501fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
3511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
3521fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @return The maximum amount this scroll view will scroll in response to
3531fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *   an arrow event.
3541fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
3551fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public int getMaxScrollAmount() {
3561fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return (int) (MAX_SCROLL_FACTOR * getHeight());
3571fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
3581fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
3591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private void initScrollView() {
3603035be16658d7652fdc472f971c81d8f7ffb60fdAurimas Liutikas        mScroller = new OverScroller(getContext());
3611fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        setFocusable(true);
3621fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
3631fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        setWillNotDraw(false);
3641fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        final ViewConfiguration configuration = ViewConfiguration.get(getContext());
3651fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        mTouchSlop = configuration.getScaledTouchSlop();
3661fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
3671fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
3681fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
3691fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
3701fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
3711fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public void addView(View child) {
3721fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (getChildCount() > 0) {
3731fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            throw new IllegalStateException("ScrollView can host only one direct child");
3741fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
3751fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
3761fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        super.addView(child);
3771fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
3781fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
3791fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
3801fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public void addView(View child, int index) {
3811fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (getChildCount() > 0) {
3821fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            throw new IllegalStateException("ScrollView can host only one direct child");
3831fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
3841fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
3851fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        super.addView(child, index);
3861fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
3871fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
3881fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
3891fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public void addView(View child, ViewGroup.LayoutParams params) {
3901fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (getChildCount() > 0) {
3911fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            throw new IllegalStateException("ScrollView can host only one direct child");
3921fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
3931fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
3941fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        super.addView(child, params);
3951fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
3961fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
3971fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
3981fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public void addView(View child, int index, ViewGroup.LayoutParams params) {
3991fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (getChildCount() > 0) {
4001fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            throw new IllegalStateException("ScrollView can host only one direct child");
4011fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
4021fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
4031fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        super.addView(child, index, params);
4041fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
4051fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
4061fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
407cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes     * Register a callback to be invoked when the scroll X or Y positions of
408cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes     * this view change.
409cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes     * <p>This version of the method works on all versions of Android, back to API v4.</p>
410cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes     *
411cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes     * @param l The listener to notify when the scroll X or Y position changes.
412cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes     * @see android.view.View#getScrollX()
413cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes     * @see android.view.View#getScrollY()
414cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes     */
415cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes    public void setOnScrollChangeListener(OnScrollChangeListener l) {
416cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes        mOnScrollChangeListener = l;
417cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes    }
418cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes
419cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes    /**
4201fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @return Returns true this ScrollView can be scrolled
4211fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
4221fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private boolean canScroll() {
4231fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        View child = getChildAt(0);
4241fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (child != null) {
4251fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            int childHeight = child.getHeight();
4261fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            return getHeight() < childHeight + getPaddingTop() + getPaddingBottom();
4271fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
4281fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return false;
4291fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
4301fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
4311fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
4321fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * Indicates whether this ScrollView's content is stretched to fill the viewport.
4331fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *
4341fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @return True if the content fills the viewport, false otherwise.
4351fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *
436929f27aab7ac7231f3734c988d5ee7201627d535Alan Viverette     * @attr name android:fillViewport
4371fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
4381fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public boolean isFillViewport() {
4391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return mFillViewport;
4401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
4411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
4421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
443236bf5bc5041e4bf356eac3855fd4fcf72cd6994Aurimas Liutikas     * Set whether this ScrollView should stretch its content height to fill the viewport or not.
4441fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *
4451fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @param fillViewport True to stretch the content's height to the viewport's
4461fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *        boundaries, false otherwise.
4471fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *
448929f27aab7ac7231f3734c988d5ee7201627d535Alan Viverette     * @attr name android:fillViewport
4491fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
4501fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public void setFillViewport(boolean fillViewport) {
4511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (fillViewport != mFillViewport) {
4521fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            mFillViewport = fillViewport;
4531fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            requestLayout();
4541fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
4551fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
4561fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
4571fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
4581fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @return Whether arrow scrolling will animate its transition.
4591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
4601fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public boolean isSmoothScrollingEnabled() {
4611fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return mSmoothScrollingEnabled;
4621fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
4631fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
4641fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
4651fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * Set whether arrow scrolling will animate its transition.
4661fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @param smoothScrollingEnabled whether arrow scrolling will animate its transition
4671fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
4681fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public void setSmoothScrollingEnabled(boolean smoothScrollingEnabled) {
4691fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        mSmoothScrollingEnabled = smoothScrollingEnabled;
4701fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
4711fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
4721fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
473cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
474cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes        super.onScrollChanged(l, t, oldl, oldt);
475cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes
476cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes        if (mOnScrollChangeListener != null) {
477cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes            mOnScrollChangeListener.onScrollChange(this, l, t, oldl, oldt);
478cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes        }
479cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes    }
480cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes
481cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes    @Override
4821fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
4831fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
4841fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
4851fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (!mFillViewport) {
4861fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            return;
4871fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
4881fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
4891fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
4901fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (heightMode == MeasureSpec.UNSPECIFIED) {
4911fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            return;
4921fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
4931fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
4941fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (getChildCount() > 0) {
4951fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            final View child = getChildAt(0);
4961fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            int height = getMeasuredHeight();
4971fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            if (child.getMeasuredHeight() < height) {
4981fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                final FrameLayout.LayoutParams lp = (LayoutParams) child.getLayoutParams();
4991fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
5001fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                int childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
5011fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        getPaddingLeft() + getPaddingRight(), lp.width);
5021fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                height -= getPaddingTop();
5031fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                height -= getPaddingBottom();
5041fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                int childHeightMeasureSpec =
5051fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
5061fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
5071fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
5081fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
5091fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
5101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
5111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
5121fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
5131fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public boolean dispatchKeyEvent(KeyEvent event) {
5141fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        // Let the focused view and/or our descendants get the key first
5151fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return super.dispatchKeyEvent(event) || executeKeyEvent(event);
5161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
5171fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
5181fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
5191fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * You can call this function yourself to have the scroll view perform
5201fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * scrolling from a key event, just as if the event had been dispatched to
5211fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * it by the view hierarchy.
5221fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *
5231fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @param event The key event to execute.
5241fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @return Return true if the event was handled, else false.
5251fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
5261fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public boolean executeKeyEvent(KeyEvent event) {
5271fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        mTempRect.setEmpty();
5281fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
5291fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (!canScroll()) {
5301fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            if (isFocused() && event.getKeyCode() != KeyEvent.KEYCODE_BACK) {
5311fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                View currentFocused = findFocus();
5321fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                if (currentFocused == this) currentFocused = null;
5331fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                View nextFocused = FocusFinder.getInstance().findNextFocus(this,
5341fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        currentFocused, View.FOCUS_DOWN);
5351fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                return nextFocused != null
5361fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        && nextFocused != this
5371fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        && nextFocused.requestFocus(View.FOCUS_DOWN);
5381fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
5391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            return false;
5401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
5411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
5421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        boolean handled = false;
5431fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (event.getAction() == KeyEvent.ACTION_DOWN) {
5441fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            switch (event.getKeyCode()) {
5451fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                case KeyEvent.KEYCODE_DPAD_UP:
5461fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    if (!event.isAltPressed()) {
5471fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        handled = arrowScroll(View.FOCUS_UP);
5481fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    } else {
5491fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        handled = fullScroll(View.FOCUS_UP);
5501fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    }
5511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    break;
5521fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                case KeyEvent.KEYCODE_DPAD_DOWN:
5531fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    if (!event.isAltPressed()) {
5541fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        handled = arrowScroll(View.FOCUS_DOWN);
5551fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    } else {
5561fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        handled = fullScroll(View.FOCUS_DOWN);
5571fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    }
5581fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    break;
5591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                case KeyEvent.KEYCODE_SPACE:
5601fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    pageScroll(event.isShiftPressed() ? View.FOCUS_UP : View.FOCUS_DOWN);
5611fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    break;
5621fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
5631fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
5641fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
5651fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return handled;
5661fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
5671fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
5681fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private boolean inChild(int x, int y) {
5691fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (getChildCount() > 0) {
5701fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            final int scrollY = getScrollY();
5711fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            final View child = getChildAt(0);
5721fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            return !(y < child.getTop() - scrollY
5731fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    || y >= child.getBottom() - scrollY
5741fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    || x < child.getLeft()
5751fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    || x >= child.getRight());
5761fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
5771fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return false;
5781fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
5791fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
5801fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private void initOrResetVelocityTracker() {
5811fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (mVelocityTracker == null) {
5821fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            mVelocityTracker = VelocityTracker.obtain();
5831fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        } else {
5841fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            mVelocityTracker.clear();
5851fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
5861fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
5871fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
5881fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private void initVelocityTrackerIfNotExists() {
5891fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (mVelocityTracker == null) {
5901fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            mVelocityTracker = VelocityTracker.obtain();
5911fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
5921fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
5931fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
5941fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private void recycleVelocityTracker() {
5951fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (mVelocityTracker != null) {
5961fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            mVelocityTracker.recycle();
5971fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            mVelocityTracker = null;
5981fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
5991fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
6001fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
6011fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
6021fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
6031fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (disallowIntercept) {
6041fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            recycleVelocityTracker();
6051fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
6061fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        super.requestDisallowInterceptTouchEvent(disallowIntercept);
6071fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
6081fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
6091fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
6101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
6111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public boolean onInterceptTouchEvent(MotionEvent ev) {
6121fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        /*
6131fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell         * This method JUST determines whether we want to intercept the motion.
6141fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell         * If we return true, onMotionEvent will be called and we do the actual
6151fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell         * scrolling there.
6161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell         */
6171fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
6181fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        /*
6191fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        * Shortcut the most recurring case: the user is in the dragging
6201fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        * state and he is moving his finger.  We want to intercept this
6211fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        * motion.
6221fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        */
6231fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        final int action = ev.getAction();
6241fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if ((action == MotionEvent.ACTION_MOVE) && (mIsBeingDragged)) {
6251fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            return true;
6261fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
6271fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
6286ed40c1f86bcb172a1f0f069cde1c571a7781aeeAurimas Liutikas        switch (action & MotionEvent.ACTION_MASK) {
6291fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            case MotionEvent.ACTION_MOVE: {
6301fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                /*
6311fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                 * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check
6321fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                 * whether the user has moved far enough from his original down touch.
6331fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                 */
6341fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
6351fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                /*
6361fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                * Locally do absolute value. mLastMotionY is set to the y value
6371fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                * of the down event.
6381fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                */
6391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                final int activePointerId = mActivePointerId;
6401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                if (activePointerId == INVALID_POINTER) {
6411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    // If we don't have a valid id, the touch down wasn't on content.
6421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    break;
6431fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                }
6441fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
6451b3e9466b4c4d72f28bb4448672ef8bab19b6f3eKirill Grouchnikov                final int pointerIndex = ev.findPointerIndex(activePointerId);
6461fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                if (pointerIndex == -1) {
6471fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    Log.e(TAG, "Invalid pointerId=" + activePointerId
6481fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                            + " in onInterceptTouchEvent");
6491fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    break;
6501fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                }
6511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
6521b3e9466b4c4d72f28bb4448672ef8bab19b6f3eKirill Grouchnikov                final int y = (int) ev.getY(pointerIndex);
6531fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                final int yDiff = Math.abs(y - mLastMotionY);
6541fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                if (yDiff > mTouchSlop
6551fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        && (getNestedScrollAxes() & ViewCompat.SCROLL_AXIS_VERTICAL) == 0) {
6561fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    mIsBeingDragged = true;
6571fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    mLastMotionY = y;
6581fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    initVelocityTrackerIfNotExists();
6591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    mVelocityTracker.addMovement(ev);
6601fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    mNestedYOffset = 0;
6611fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    final ViewParent parent = getParent();
6621fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    if (parent != null) {
6631fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        parent.requestDisallowInterceptTouchEvent(true);
6641fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    }
6651fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                }
6661fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                break;
6671fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
6681fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
6691fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            case MotionEvent.ACTION_DOWN: {
6701fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                final int y = (int) ev.getY();
6711fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                if (!inChild((int) ev.getX(), (int) y)) {
6721fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    mIsBeingDragged = false;
6731fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    recycleVelocityTracker();
6741fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    break;
6751fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                }
6761fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
6771fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                /*
6781fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                 * Remember location of down touch.
6791fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                 * ACTION_DOWN always refers to pointer index 0.
6801fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                 */
6811fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                mLastMotionY = y;
6821b3e9466b4c4d72f28bb4448672ef8bab19b6f3eKirill Grouchnikov                mActivePointerId = ev.getPointerId(0);
6831fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
6841fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                initOrResetVelocityTracker();
6851fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                mVelocityTracker.addMovement(ev);
6861fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                /*
687661d25de05417a357540283ee0cd93b1326ac3a4Chris Banes                 * If being flinged and user touches the screen, initiate drag;
688661d25de05417a357540283ee0cd93b1326ac3a4Chris Banes                 * otherwise don't. mScroller.isFinished should be false when
689661d25de05417a357540283ee0cd93b1326ac3a4Chris Banes                 * being flinged. We need to call computeScrollOffset() first so that
690661d25de05417a357540283ee0cd93b1326ac3a4Chris Banes                 * isFinished() is correct.
6911fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                */
692661d25de05417a357540283ee0cd93b1326ac3a4Chris Banes                mScroller.computeScrollOffset();
6931fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                mIsBeingDragged = !mScroller.isFinished();
6941fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL);
6951fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                break;
6961fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
6971fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
6981fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            case MotionEvent.ACTION_CANCEL:
6991fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            case MotionEvent.ACTION_UP:
7001fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                /* Release the drag */
7011fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                mIsBeingDragged = false;
7021fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                mActivePointerId = INVALID_POINTER;
7031fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                recycleVelocityTracker();
70400db92e217c3bc08acd09143cac8e3d3b0d0e813Chris Banes                if (mScroller.springBack(getScrollX(), getScrollY(), 0, 0, 0, getScrollRange())) {
70500db92e217c3bc08acd09143cac8e3d3b0d0e813Chris Banes                    ViewCompat.postInvalidateOnAnimation(this);
70600db92e217c3bc08acd09143cac8e3d3b0d0e813Chris Banes                }
7071fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                stopNestedScroll();
7081fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                break;
7096ed40c1f86bcb172a1f0f069cde1c571a7781aeeAurimas Liutikas            case MotionEvent.ACTION_POINTER_UP:
7101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                onSecondaryPointerUp(ev);
7111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                break;
7121fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
7131fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
7141fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        /*
7151fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        * The only time we want to intercept motion events is if we are in the
7161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        * drag mode.
7171fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        */
7181fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return mIsBeingDragged;
7191fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
7201fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
7211fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
7221fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public boolean onTouchEvent(MotionEvent ev) {
7231fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        initVelocityTrackerIfNotExists();
7241fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
7251fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        MotionEvent vtev = MotionEvent.obtain(ev);
7261fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
7276ed40c1f86bcb172a1f0f069cde1c571a7781aeeAurimas Liutikas        final int actionMasked = ev.getActionMasked();
7281fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
7291fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (actionMasked == MotionEvent.ACTION_DOWN) {
7301fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            mNestedYOffset = 0;
7311fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
7321fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        vtev.offsetLocation(0, mNestedYOffset);
7331fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
7341fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        switch (actionMasked) {
7351fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            case MotionEvent.ACTION_DOWN: {
7361fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                if (getChildCount() == 0) {
7371fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    return false;
7381fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                }
7391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                if ((mIsBeingDragged = !mScroller.isFinished())) {
7401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    final ViewParent parent = getParent();
7411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    if (parent != null) {
7421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        parent.requestDisallowInterceptTouchEvent(true);
7431fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    }
7441fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                }
7451fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
7461fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                /*
7471fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                 * If being flinged and user touches, stop the fling. isFinished
7481fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                 * will be false if being flinged.
7491fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                 */
7501fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                if (!mScroller.isFinished()) {
7511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    mScroller.abortAnimation();
7521fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                }
7531fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
7541fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                // Remember where the motion event started
7551fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                mLastMotionY = (int) ev.getY();
7561b3e9466b4c4d72f28bb4448672ef8bab19b6f3eKirill Grouchnikov                mActivePointerId = ev.getPointerId(0);
7571fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL);
7581fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                break;
7591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
7601fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            case MotionEvent.ACTION_MOVE:
7611b3e9466b4c4d72f28bb4448672ef8bab19b6f3eKirill Grouchnikov                final int activePointerIndex = ev.findPointerIndex(mActivePointerId);
7621fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                if (activePointerIndex == -1) {
7631fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    Log.e(TAG, "Invalid pointerId=" + mActivePointerId + " in onTouchEvent");
7641fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    break;
7651fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                }
7661fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
7671b3e9466b4c4d72f28bb4448672ef8bab19b6f3eKirill Grouchnikov                final int y = (int) ev.getY(activePointerIndex);
7681fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                int deltaY = mLastMotionY - y;
7691fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                if (dispatchNestedPreScroll(0, deltaY, mScrollConsumed, mScrollOffset)) {
7701fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    deltaY -= mScrollConsumed[1];
7711fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    vtev.offsetLocation(0, mScrollOffset[1]);
7721fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    mNestedYOffset += mScrollOffset[1];
7731fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                }
7741fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                if (!mIsBeingDragged && Math.abs(deltaY) > mTouchSlop) {
7751fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    final ViewParent parent = getParent();
7761fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    if (parent != null) {
7771fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        parent.requestDisallowInterceptTouchEvent(true);
7781fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    }
7791fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    mIsBeingDragged = true;
7801fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    if (deltaY > 0) {
7811fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        deltaY -= mTouchSlop;
7821fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    } else {
7831fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        deltaY += mTouchSlop;
7841fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    }
7851fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                }
7861fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                if (mIsBeingDragged) {
7871fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    // Scroll to follow the motion event
7881fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    mLastMotionY = y - mScrollOffset[1];
7891fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
7901fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    final int oldY = getScrollY();
7911fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    final int range = getScrollRange();
792e12dfa03641ad9cf0ddf272675bbe7d1198adbfdAurimas Liutikas                    final int overscrollMode = getOverScrollMode();
793e12dfa03641ad9cf0ddf272675bbe7d1198adbfdAurimas Liutikas                    boolean canOverscroll = overscrollMode == View.OVER_SCROLL_ALWAYS
794e12dfa03641ad9cf0ddf272675bbe7d1198adbfdAurimas Liutikas                            || (overscrollMode == View.OVER_SCROLL_IF_CONTENT_SCROLLS && range > 0);
7951fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
7961fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    // Calling overScrollByCompat will call onOverScrolled, which
7971fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    // calls onScrollChanged if applicable.
7981fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    if (overScrollByCompat(0, deltaY, 0, getScrollY(), 0, range, 0,
7991fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                            0, true) && !hasNestedScrollingParent()) {
8001fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        // Break our velocity if we hit a scroll barrier.
8011fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        mVelocityTracker.clear();
8021fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    }
8031fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
8041fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    final int scrolledDeltaY = getScrollY() - oldY;
8051fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    final int unconsumedY = deltaY - scrolledDeltaY;
8061fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    if (dispatchNestedScroll(0, scrolledDeltaY, 0, unconsumedY, mScrollOffset)) {
8071fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        mLastMotionY -= mScrollOffset[1];
8081fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        vtev.offsetLocation(0, mScrollOffset[1]);
8091fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        mNestedYOffset += mScrollOffset[1];
8101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    } else if (canOverscroll) {
8111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        ensureGlows();
8121fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        final int pulledToY = oldY + deltaY;
8131fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        if (pulledToY < 0) {
814506695b4cf6b22d70d2e3647d94551e721ee0430Aurimas Liutikas                            EdgeEffectCompat.onPull(mEdgeGlowTop, (float) deltaY / getHeight(),
8151b3e9466b4c4d72f28bb4448672ef8bab19b6f3eKirill Grouchnikov                                    ev.getX(activePointerIndex) / getWidth());
8161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                            if (!mEdgeGlowBottom.isFinished()) {
8171fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                                mEdgeGlowBottom.onRelease();
8181fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                            }
8191fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        } else if (pulledToY > range) {
820506695b4cf6b22d70d2e3647d94551e721ee0430Aurimas Liutikas                            EdgeEffectCompat.onPull(mEdgeGlowBottom, (float) deltaY / getHeight(),
8211b3e9466b4c4d72f28bb4448672ef8bab19b6f3eKirill Grouchnikov                                    1.f - ev.getX(activePointerIndex)
8221fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                                            / getWidth());
8231fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                            if (!mEdgeGlowTop.isFinished()) {
8241fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                                mEdgeGlowTop.onRelease();
8251fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                            }
8261fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        }
8271fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        if (mEdgeGlowTop != null
8281fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                                && (!mEdgeGlowTop.isFinished() || !mEdgeGlowBottom.isFinished())) {
8291fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                            ViewCompat.postInvalidateOnAnimation(this);
8301fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        }
8311fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    }
8321fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                }
8331fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                break;
8341fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            case MotionEvent.ACTION_UP:
8351fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                if (mIsBeingDragged) {
8361fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    final VelocityTracker velocityTracker = mVelocityTracker;
8371fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
838374cad6778dfb6923f35bfc28290a9baad22743eAurimas Liutikas                    int initialVelocity = (int) velocityTracker.getYVelocity(mActivePointerId);
8391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
8401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    if ((Math.abs(initialVelocity) > mMinimumVelocity)) {
8411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        flingWithNestedDispatch(-initialVelocity);
84200db92e217c3bc08acd09143cac8e3d3b0d0e813Chris Banes                    } else if (mScroller.springBack(getScrollX(), getScrollY(), 0, 0, 0,
84300db92e217c3bc08acd09143cac8e3d3b0d0e813Chris Banes                            getScrollRange())) {
84400db92e217c3bc08acd09143cac8e3d3b0d0e813Chris Banes                        ViewCompat.postInvalidateOnAnimation(this);
8451fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    }
8461fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                }
847ba3bd1923ffd1ef5597deb0a9df8c1444fbf4433Chris Banes                mActivePointerId = INVALID_POINTER;
848ba3bd1923ffd1ef5597deb0a9df8c1444fbf4433Chris Banes                endDrag();
8491fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                break;
8501fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            case MotionEvent.ACTION_CANCEL:
8511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                if (mIsBeingDragged && getChildCount() > 0) {
85200db92e217c3bc08acd09143cac8e3d3b0d0e813Chris Banes                    if (mScroller.springBack(getScrollX(), getScrollY(), 0, 0, 0,
85300db92e217c3bc08acd09143cac8e3d3b0d0e813Chris Banes                            getScrollRange())) {
85400db92e217c3bc08acd09143cac8e3d3b0d0e813Chris Banes                        ViewCompat.postInvalidateOnAnimation(this);
85500db92e217c3bc08acd09143cac8e3d3b0d0e813Chris Banes                    }
8561fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                }
857ba3bd1923ffd1ef5597deb0a9df8c1444fbf4433Chris Banes                mActivePointerId = INVALID_POINTER;
858ba3bd1923ffd1ef5597deb0a9df8c1444fbf4433Chris Banes                endDrag();
8591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                break;
8606ed40c1f86bcb172a1f0f069cde1c571a7781aeeAurimas Liutikas            case MotionEvent.ACTION_POINTER_DOWN: {
8616ed40c1f86bcb172a1f0f069cde1c571a7781aeeAurimas Liutikas                final int index = ev.getActionIndex();
8621b3e9466b4c4d72f28bb4448672ef8bab19b6f3eKirill Grouchnikov                mLastMotionY = (int) ev.getY(index);
8631b3e9466b4c4d72f28bb4448672ef8bab19b6f3eKirill Grouchnikov                mActivePointerId = ev.getPointerId(index);
8641fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                break;
8651fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
8666ed40c1f86bcb172a1f0f069cde1c571a7781aeeAurimas Liutikas            case MotionEvent.ACTION_POINTER_UP:
8671fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                onSecondaryPointerUp(ev);
8681b3e9466b4c4d72f28bb4448672ef8bab19b6f3eKirill Grouchnikov                mLastMotionY = (int) ev.getY(ev.findPointerIndex(mActivePointerId));
8691fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                break;
8701fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
8711fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
8721fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (mVelocityTracker != null) {
8731fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            mVelocityTracker.addMovement(vtev);
8741fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
8751fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        vtev.recycle();
8761fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return true;
8771fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
8781fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
8791fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private void onSecondaryPointerUp(MotionEvent ev) {
8806ed40c1f86bcb172a1f0f069cde1c571a7781aeeAurimas Liutikas        final int pointerIndex = ev.getActionIndex();
8811b3e9466b4c4d72f28bb4448672ef8bab19b6f3eKirill Grouchnikov        final int pointerId = ev.getPointerId(pointerIndex);
8821fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (pointerId == mActivePointerId) {
8831fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            // This was our active pointer going up. Choose a new
8841fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            // active pointer and adjust accordingly.
8851fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            // TODO: Make this decision more intelligent.
8861fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
8871b3e9466b4c4d72f28bb4448672ef8bab19b6f3eKirill Grouchnikov            mLastMotionY = (int) ev.getY(newPointerIndex);
8881b3e9466b4c4d72f28bb4448672ef8bab19b6f3eKirill Grouchnikov            mActivePointerId = ev.getPointerId(newPointerIndex);
8891fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            if (mVelocityTracker != null) {
8901fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                mVelocityTracker.clear();
8911fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
8921fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
8931fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
8941fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
895e2104f4b5c8e3ad63570306a25e61502dfe4c418Aurimas Liutikas    @Override
8961fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public boolean onGenericMotionEvent(MotionEvent event) {
8971b3e9466b4c4d72f28bb4448672ef8bab19b6f3eKirill Grouchnikov        if ((event.getSource() & InputDeviceCompat.SOURCE_CLASS_POINTER) != 0) {
8981fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            switch (event.getAction()) {
8996ed40c1f86bcb172a1f0f069cde1c571a7781aeeAurimas Liutikas                case MotionEvent.ACTION_SCROLL: {
9001fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    if (!mIsBeingDragged) {
9016ed40c1f86bcb172a1f0f069cde1c571a7781aeeAurimas Liutikas                        final float vscroll = event.getAxisValue(MotionEvent.AXIS_VSCROLL);
9021fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        if (vscroll != 0) {
9031fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                            final int delta = (int) (vscroll * getVerticalScrollFactorCompat());
9041fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                            final int range = getScrollRange();
9051fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                            int oldScrollY = getScrollY();
9061fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                            int newScrollY = oldScrollY - delta;
9071fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                            if (newScrollY < 0) {
9081fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                                newScrollY = 0;
9091fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                            } else if (newScrollY > range) {
9101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                                newScrollY = range;
9111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                            }
9121fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                            if (newScrollY != oldScrollY) {
9131fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                                super.scrollTo(getScrollX(), newScrollY);
9141fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                                return true;
9151fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                            }
9161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        }
9171fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    }
9181fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                }
9191fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
9201fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
9211fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return false;
9221fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
9231fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
9241fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private float getVerticalScrollFactorCompat() {
9251fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (mVerticalScrollFactor == 0) {
9261fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            TypedValue outValue = new TypedValue();
9271fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            final Context context = getContext();
9281fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            if (!context.getTheme().resolveAttribute(
9291fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    android.R.attr.listPreferredItemHeight, outValue, true)) {
9301fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                throw new IllegalStateException(
9311fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        "Expected theme to define listPreferredItemHeight.");
9321fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
9331fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            mVerticalScrollFactor = outValue.getDimension(
9341fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    context.getResources().getDisplayMetrics());
9351fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
9361fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return mVerticalScrollFactor;
9371fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
9381fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
93915375aa6fd54b036f97f99229aefab2822c8a1c9Aurimas Liutikas    @Override
9401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    protected void onOverScrolled(int scrollX, int scrollY,
9411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            boolean clampedX, boolean clampedY) {
9421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        super.scrollTo(scrollX, scrollY);
9431fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
9441fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
9451fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    boolean overScrollByCompat(int deltaX, int deltaY,
9461fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            int scrollX, int scrollY,
9471fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            int scrollRangeX, int scrollRangeY,
9481fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            int maxOverScrollX, int maxOverScrollY,
9491fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            boolean isTouchEvent) {
950e12dfa03641ad9cf0ddf272675bbe7d1198adbfdAurimas Liutikas        final int overScrollMode = getOverScrollMode();
9511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        final boolean canScrollHorizontal =
9521fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                computeHorizontalScrollRange() > computeHorizontalScrollExtent();
9531fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        final boolean canScrollVertical =
9541fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                computeVerticalScrollRange() > computeVerticalScrollExtent();
955e12dfa03641ad9cf0ddf272675bbe7d1198adbfdAurimas Liutikas        final boolean overScrollHorizontal = overScrollMode == View.OVER_SCROLL_ALWAYS
956e12dfa03641ad9cf0ddf272675bbe7d1198adbfdAurimas Liutikas                || (overScrollMode == View.OVER_SCROLL_IF_CONTENT_SCROLLS && canScrollHorizontal);
957e12dfa03641ad9cf0ddf272675bbe7d1198adbfdAurimas Liutikas        final boolean overScrollVertical = overScrollMode == View.OVER_SCROLL_ALWAYS
958e12dfa03641ad9cf0ddf272675bbe7d1198adbfdAurimas Liutikas                || (overScrollMode == View.OVER_SCROLL_IF_CONTENT_SCROLLS && canScrollVertical);
9591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
9601fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        int newScrollX = scrollX + deltaX;
9611fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (!overScrollHorizontal) {
9621fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            maxOverScrollX = 0;
9631fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
9641fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
9651fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        int newScrollY = scrollY + deltaY;
9661fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (!overScrollVertical) {
9671fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            maxOverScrollY = 0;
9681fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
9691fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
9701fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        // Clamp values if at the limits and record
9711fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        final int left = -maxOverScrollX;
9721fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        final int right = maxOverScrollX + scrollRangeX;
9731fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        final int top = -maxOverScrollY;
9741fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        final int bottom = maxOverScrollY + scrollRangeY;
9751fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
9761fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        boolean clampedX = false;
9771fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (newScrollX > right) {
9781fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            newScrollX = right;
9791fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            clampedX = true;
9801fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        } else if (newScrollX < left) {
9811fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            newScrollX = left;
9821fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            clampedX = true;
9831fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
9841fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
9851fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        boolean clampedY = false;
9861fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (newScrollY > bottom) {
9871fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            newScrollY = bottom;
9881fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            clampedY = true;
9891fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        } else if (newScrollY < top) {
9901fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            newScrollY = top;
9911fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            clampedY = true;
9921fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
9931fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
99400db92e217c3bc08acd09143cac8e3d3b0d0e813Chris Banes        if (clampedY) {
99500db92e217c3bc08acd09143cac8e3d3b0d0e813Chris Banes            mScroller.springBack(newScrollX, newScrollY, 0, 0, 0, getScrollRange());
99600db92e217c3bc08acd09143cac8e3d3b0d0e813Chris Banes        }
99700db92e217c3bc08acd09143cac8e3d3b0d0e813Chris Banes
9981fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        onOverScrolled(newScrollX, newScrollY, clampedX, clampedY);
9991fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
10001fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return clampedX || clampedY;
10011fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
10021fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
1003540222c3675801eaa141ace1c164c4d3499b4721Aurimas Liutikas    int getScrollRange() {
10041fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        int scrollRange = 0;
10051fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (getChildCount() > 0) {
10061fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            View child = getChildAt(0);
10071fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            scrollRange = Math.max(0,
10081fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    child.getHeight() - (getHeight() - getPaddingBottom() - getPaddingTop()));
10091fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
10101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return scrollRange;
10111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
10121fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
10131fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
10141fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * <p>
10151fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * Finds the next focusable component that fits in the specified bounds.
10161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * </p>
10171fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *
10181fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @param topFocus look for a candidate is the one at the top of the bounds
10191fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *                 if topFocus is true, or at the bottom of the bounds if topFocus is
10201fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *                 false
10211fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @param top      the top offset of the bounds in which a focusable must be
10221fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *                 found
10231fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @param bottom   the bottom offset of the bounds in which a focusable must
10241fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *                 be found
10251fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @return the next focusable component in the bounds or null if none can
10261fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *         be found
10271fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
10281fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private View findFocusableViewInBounds(boolean topFocus, int top, int bottom) {
10291fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
10301fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        List<View> focusables = getFocusables(View.FOCUS_FORWARD);
10311fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        View focusCandidate = null;
10321fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
10331fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        /*
10341fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell         * A fully contained focusable is one where its top is below the bound's
10351fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell         * top, and its bottom is above the bound's bottom. A partially
10361fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell         * contained focusable is one where some part of it is within the
10371fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell         * bounds, but it also has some part that is not within bounds.  A fully contained
10381fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell         * focusable is preferred to a partially contained focusable.
10391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell         */
10401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        boolean foundFullyContainedFocusable = false;
10411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
10421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        int count = focusables.size();
10431fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        for (int i = 0; i < count; i++) {
10441fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            View view = focusables.get(i);
10451fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            int viewTop = view.getTop();
10461fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            int viewBottom = view.getBottom();
10471fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
10481fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            if (top < viewBottom && viewTop < bottom) {
10491fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                /*
10501fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                 * the focusable is in the target area, it is a candidate for
10511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                 * focusing
10521fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                 */
10531fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
10544d2c7b7c4f194034c5f17c4bee7320d808aabe4cAurimas Liutikas                final boolean viewIsFullyContained = (top < viewTop) && (viewBottom < bottom);
10551fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
10561fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                if (focusCandidate == null) {
10571fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    /* No candidate, take this one */
10581fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    focusCandidate = view;
10591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    foundFullyContainedFocusable = viewIsFullyContained;
10601fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                } else {
10611fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    final boolean viewIsCloserToBoundary =
10624d2c7b7c4f194034c5f17c4bee7320d808aabe4cAurimas Liutikas                            (topFocus && viewTop < focusCandidate.getTop())
10634d2c7b7c4f194034c5f17c4bee7320d808aabe4cAurimas Liutikas                                    || (!topFocus && viewBottom > focusCandidate.getBottom());
10641fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
10651fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    if (foundFullyContainedFocusable) {
10661fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        if (viewIsFullyContained && viewIsCloserToBoundary) {
10671fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                            /*
10681fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                             * We're dealing with only fully contained views, so
10691fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                             * it has to be closer to the boundary to beat our
10701fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                             * candidate
10711fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                             */
10721fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                            focusCandidate = view;
10731fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        }
10741fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    } else {
10751fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        if (viewIsFullyContained) {
10761fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                            /* Any fully contained view beats a partially contained view */
10771fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                            focusCandidate = view;
10781fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                            foundFullyContainedFocusable = true;
10791fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        } else if (viewIsCloserToBoundary) {
10801fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                            /*
10811fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                             * Partially contained view beats another partially
10821fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                             * contained view if it's closer
10831fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                             */
10841fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                            focusCandidate = view;
10851fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        }
10861fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    }
10871fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                }
10881fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
10891fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
10901fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
10911fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return focusCandidate;
10921fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
10931fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
10941fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
10951fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * <p>Handles scrolling in response to a "page up/down" shortcut press. This
10961fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * method will scroll the view by one page up or down and give the focus
10971fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * to the topmost/bottommost component in the new visible area. If no
10981fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * component is a good candidate for focus, this scrollview reclaims the
10991fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * focus.</p>
11001fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *
11011fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @param direction the scroll direction: {@link android.view.View#FOCUS_UP}
11021fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *                  to go one page up or
11031fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *                  {@link android.view.View#FOCUS_DOWN} to go one page down
11041fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @return true if the key event is consumed by this method, false otherwise
11051fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
11061fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public boolean pageScroll(int direction) {
11071fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        boolean down = direction == View.FOCUS_DOWN;
11081fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        int height = getHeight();
11091fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
11101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (down) {
11111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            mTempRect.top = getScrollY() + height;
11121fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            int count = getChildCount();
11131fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            if (count > 0) {
11141fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                View view = getChildAt(count - 1);
11151fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                if (mTempRect.top + height > view.getBottom()) {
11161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    mTempRect.top = view.getBottom() - height;
11171fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                }
11181fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
11191fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        } else {
11201fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            mTempRect.top = getScrollY() - height;
11211fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            if (mTempRect.top < 0) {
11221fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                mTempRect.top = 0;
11231fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
11241fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
11251fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        mTempRect.bottom = mTempRect.top + height;
11261fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
11271fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return scrollAndFocus(direction, mTempRect.top, mTempRect.bottom);
11281fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
11291fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
11301fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
11311fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * <p>Handles scrolling in response to a "home/end" shortcut press. This
11321fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * method will scroll the view to the top or bottom and give the focus
11331fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * to the topmost/bottommost component in the new visible area. If no
11341fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * component is a good candidate for focus, this scrollview reclaims the
11351fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * focus.</p>
11361fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *
11371fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @param direction the scroll direction: {@link android.view.View#FOCUS_UP}
11381fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *                  to go the top of the view or
11391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *                  {@link android.view.View#FOCUS_DOWN} to go the bottom
11401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @return true if the key event is consumed by this method, false otherwise
11411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
11421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public boolean fullScroll(int direction) {
11431fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        boolean down = direction == View.FOCUS_DOWN;
11441fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        int height = getHeight();
11451fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
11461fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        mTempRect.top = 0;
11471fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        mTempRect.bottom = height;
11481fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
11491fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (down) {
11501fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            int count = getChildCount();
11511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            if (count > 0) {
11521fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                View view = getChildAt(count - 1);
11531fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                mTempRect.bottom = view.getBottom() + getPaddingBottom();
11541fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                mTempRect.top = mTempRect.bottom - height;
11551fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
11561fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
11571fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
11581fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return scrollAndFocus(direction, mTempRect.top, mTempRect.bottom);
11591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
11601fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
11611fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
11621fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * <p>Scrolls the view to make the area defined by <code>top</code> and
11631fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * <code>bottom</code> visible. This method attempts to give the focus
11641fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * to a component visible in this area. If no component can be focused in
11651fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * the new visible area, the focus is reclaimed by this ScrollView.</p>
11661fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *
11671fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @param direction the scroll direction: {@link android.view.View#FOCUS_UP}
11681fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *                  to go upward, {@link android.view.View#FOCUS_DOWN} to downward
11691fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @param top       the top offset of the new area to be made visible
11701fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @param bottom    the bottom offset of the new area to be made visible
11711fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @return true if the key event is consumed by this method, false otherwise
11721fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
11731fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private boolean scrollAndFocus(int direction, int top, int bottom) {
11741fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        boolean handled = true;
11751fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
11761fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        int height = getHeight();
11771fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        int containerTop = getScrollY();
11781fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        int containerBottom = containerTop + height;
11791fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        boolean up = direction == View.FOCUS_UP;
11801fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
11811fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        View newFocused = findFocusableViewInBounds(up, top, bottom);
11821fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (newFocused == null) {
11831fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            newFocused = this;
11841fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
11851fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
11861fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (top >= containerTop && bottom <= containerBottom) {
11871fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            handled = false;
11881fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        } else {
11891fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            int delta = up ? (top - containerTop) : (bottom - containerBottom);
11901fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            doScrollY(delta);
11911fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
11921fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
11931fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (newFocused != findFocus()) newFocused.requestFocus(direction);
11941fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
11951fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return handled;
11961fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
11971fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
11981fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
11991fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * Handle scrolling in response to an up or down arrow click.
12001fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *
12011fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @param direction The direction corresponding to the arrow key that was
12021fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *                  pressed
12031fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @return True if we consumed the event, false otherwise
12041fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
12051fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public boolean arrowScroll(int direction) {
12061fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
12071fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        View currentFocused = findFocus();
12081fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (currentFocused == this) currentFocused = null;
12091fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
12101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        View nextFocused = FocusFinder.getInstance().findNextFocus(this, currentFocused, direction);
12111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
12121fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        final int maxJump = getMaxScrollAmount();
12131fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
12141fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (nextFocused != null && isWithinDeltaOfScreen(nextFocused, maxJump, getHeight())) {
12151fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            nextFocused.getDrawingRect(mTempRect);
12161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            offsetDescendantRectToMyCoords(nextFocused, mTempRect);
12171fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            int scrollDelta = computeScrollDeltaToGetChildRectOnScreen(mTempRect);
12181fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            doScrollY(scrollDelta);
12191fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            nextFocused.requestFocus(direction);
12201fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        } else {
12211fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            // no new focus
12221fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            int scrollDelta = maxJump;
12231fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
12241fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            if (direction == View.FOCUS_UP && getScrollY() < scrollDelta) {
12251fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                scrollDelta = getScrollY();
12261fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            } else if (direction == View.FOCUS_DOWN) {
12271fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                if (getChildCount() > 0) {
12281fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    int daBottom = getChildAt(0).getBottom();
12291fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    int screenBottom = getScrollY() + getHeight() - getPaddingBottom();
12301fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    if (daBottom - screenBottom < maxJump) {
12311fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        scrollDelta = daBottom - screenBottom;
12321fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    }
12331fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                }
12341fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
12351fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            if (scrollDelta == 0) {
12361fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                return false;
12371fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
12381fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            doScrollY(direction == View.FOCUS_DOWN ? scrollDelta : -scrollDelta);
12391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
12401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
12411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (currentFocused != null && currentFocused.isFocused()
12421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                && isOffScreen(currentFocused)) {
12431fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            // previously focused item still has focus and is off screen, give
12441fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            // it up (take it back to ourselves)
12451fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            // (also, need to temporarily force FOCUS_BEFORE_DESCENDANTS so we are
12461fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            // sure to
12471fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            // get it)
12481fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            final int descendantFocusability = getDescendantFocusability();  // save
12491fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
12501fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            requestFocus();
12511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            setDescendantFocusability(descendantFocusability);  // restore
12521fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
12531fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return true;
12541fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
12551fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
12561fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
12571fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @return whether the descendant of this scroll view is scrolled off
12581fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *  screen.
12591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
12601fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private boolean isOffScreen(View descendant) {
12611fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return !isWithinDeltaOfScreen(descendant, 0, getHeight());
12621fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
12631fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
12641fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
12651fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @return whether the descendant of this scroll view is within delta
12661fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *  pixels of being on the screen.
12671fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
12681fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private boolean isWithinDeltaOfScreen(View descendant, int delta, int height) {
12691fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        descendant.getDrawingRect(mTempRect);
12701fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        offsetDescendantRectToMyCoords(descendant, mTempRect);
12711fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
12721fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return (mTempRect.bottom + delta) >= getScrollY()
12731fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                && (mTempRect.top - delta) <= (getScrollY() + height);
12741fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
12751fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
12761fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
12771fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * Smooth scroll by a Y delta
12781fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *
12791fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @param delta the number of pixels to scroll by on the Y axis
12801fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
12811fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private void doScrollY(int delta) {
12821fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (delta != 0) {
12831fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            if (mSmoothScrollingEnabled) {
12841fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                smoothScrollBy(0, delta);
12851fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            } else {
12861fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                scrollBy(0, delta);
12871fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
12881fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
12891fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
12901fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
12911fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
12921fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * Like {@link View#scrollBy}, but scroll smoothly instead of immediately.
12931fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *
12941fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @param dx the number of pixels to scroll by on the X axis
12951fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @param dy the number of pixels to scroll by on the Y axis
12961fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
12971fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public final void smoothScrollBy(int dx, int dy) {
12981fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (getChildCount() == 0) {
12991fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            // Nothing to do.
13001fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            return;
13011fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
13021fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        long duration = AnimationUtils.currentAnimationTimeMillis() - mLastScroll;
13031fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (duration > ANIMATED_SCROLL_GAP) {
13041fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            final int height = getHeight() - getPaddingBottom() - getPaddingTop();
13051fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            final int bottom = getChildAt(0).getHeight();
13061fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            final int maxY = Math.max(0, bottom - height);
13071fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            final int scrollY = getScrollY();
13081fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            dy = Math.max(0, Math.min(scrollY + dy, maxY)) - scrollY;
13091fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
13101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            mScroller.startScroll(getScrollX(), scrollY, 0, dy);
13111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            ViewCompat.postInvalidateOnAnimation(this);
13121fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        } else {
13131fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            if (!mScroller.isFinished()) {
13141fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                mScroller.abortAnimation();
13151fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
13161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            scrollBy(dx, dy);
13171fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
13181fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        mLastScroll = AnimationUtils.currentAnimationTimeMillis();
13191fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
13201fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
13211fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
13221fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * Like {@link #scrollTo}, but scroll smoothly instead of immediately.
13231fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *
13241fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @param x the position where to scroll on the X axis
13251fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @param y the position where to scroll on the Y axis
13261fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
13271fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public final void smoothScrollTo(int x, int y) {
13281fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        smoothScrollBy(x - getScrollX(), y - getScrollY());
13291fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
13301fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
13311fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
13321fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * <p>The scroll range of a scroll view is the overall height of all of its
13331fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * children.</p>
1334c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki     * @hide
13351fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
13368e10080c914d1ad0784394fa3026b85535535847Aurimas Liutikas    @RestrictTo(LIBRARY_GROUP)
13371fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
1338c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki    public int computeVerticalScrollRange() {
13391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        final int count = getChildCount();
13401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        final int contentHeight = getHeight() - getPaddingBottom() - getPaddingTop();
13411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (count == 0) {
13421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            return contentHeight;
13431fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
13441fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
13451fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        int scrollRange = getChildAt(0).getBottom();
13461fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        final int scrollY = getScrollY();
13471fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        final int overscrollBottom = Math.max(0, scrollRange - contentHeight);
13481fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (scrollY < 0) {
13491fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            scrollRange -= scrollY;
13501fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        } else if (scrollY > overscrollBottom) {
13511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            scrollRange += scrollY - overscrollBottom;
13521fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
13531fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
13541fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return scrollRange;
13551fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
13561fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
1357c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki    /** @hide */
13588e10080c914d1ad0784394fa3026b85535535847Aurimas Liutikas    @RestrictTo(LIBRARY_GROUP)
13591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
1360c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki    public int computeVerticalScrollOffset() {
13611fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return Math.max(0, super.computeVerticalScrollOffset());
13621fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
13631fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
1364c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki    /** @hide */
13658e10080c914d1ad0784394fa3026b85535535847Aurimas Liutikas    @RestrictTo(LIBRARY_GROUP)
1366c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki    @Override
1367c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki    public int computeVerticalScrollExtent() {
1368c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki        return super.computeVerticalScrollExtent();
1369c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki    }
1370c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki
1371c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki    /** @hide */
13728e10080c914d1ad0784394fa3026b85535535847Aurimas Liutikas    @RestrictTo(LIBRARY_GROUP)
1373c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki    @Override
1374c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki    public int computeHorizontalScrollRange() {
1375c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki        return super.computeHorizontalScrollRange();
1376c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki    }
1377c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki
1378c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki    /** @hide */
13798e10080c914d1ad0784394fa3026b85535535847Aurimas Liutikas    @RestrictTo(LIBRARY_GROUP)
1380c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki    @Override
1381c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki    public int computeHorizontalScrollOffset() {
1382c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki        return super.computeHorizontalScrollOffset();
1383c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki    }
1384c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki
1385c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki    /** @hide */
13868e10080c914d1ad0784394fa3026b85535535847Aurimas Liutikas    @RestrictTo(LIBRARY_GROUP)
1387c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki    @Override
1388c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki    public int computeHorizontalScrollExtent() {
1389c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki        return super.computeHorizontalScrollExtent();
1390c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki    }
1391c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki
13921fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
13934d2c7b7c4f194034c5f17c4bee7320d808aabe4cAurimas Liutikas    protected void measureChild(View child, int parentWidthMeasureSpec,
13944d2c7b7c4f194034c5f17c4bee7320d808aabe4cAurimas Liutikas            int parentHeightMeasureSpec) {
13951fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        ViewGroup.LayoutParams lp = child.getLayoutParams();
13961fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
13971fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        int childWidthMeasureSpec;
13981fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        int childHeightMeasureSpec;
13991fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
14001fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, getPaddingLeft()
14011fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                + getPaddingRight(), lp.width);
14021fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
14031fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
14041fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
14051fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
14061fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
14071fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
14081fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
14091fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed,
14101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            int parentHeightMeasureSpec, int heightUsed) {
14111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
14121fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
14131fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
14141fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                getPaddingLeft() + getPaddingRight() + lp.leftMargin + lp.rightMargin
14151fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        + widthUsed, lp.width);
14161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        final int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
14171fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                lp.topMargin + lp.bottomMargin, MeasureSpec.UNSPECIFIED);
14181fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
14191fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
14201fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
14211fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
14221fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
14231fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public void computeScroll() {
14241fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (mScroller.computeScrollOffset()) {
14251fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            int oldX = getScrollX();
14261fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            int oldY = getScrollY();
14271fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            int x = mScroller.getCurrX();
14281fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            int y = mScroller.getCurrY();
14291fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
14301fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            if (oldX != x || oldY != y) {
14311fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                final int range = getScrollRange();
1432e12dfa03641ad9cf0ddf272675bbe7d1198adbfdAurimas Liutikas                final int overscrollMode = getOverScrollMode();
1433e12dfa03641ad9cf0ddf272675bbe7d1198adbfdAurimas Liutikas                final boolean canOverscroll = overscrollMode == View.OVER_SCROLL_ALWAYS
1434e12dfa03641ad9cf0ddf272675bbe7d1198adbfdAurimas Liutikas                        || (overscrollMode == View.OVER_SCROLL_IF_CONTENT_SCROLLS && range > 0);
14351fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
14361fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                overScrollByCompat(x - oldX, y - oldY, oldX, oldY, 0, range,
14371fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        0, 0, false);
14381fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
14391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                if (canOverscroll) {
14401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    ensureGlows();
14411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    if (y <= 0 && oldY > 0) {
14421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        mEdgeGlowTop.onAbsorb((int) mScroller.getCurrVelocity());
14431fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    } else if (y >= range && oldY < range) {
14441fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        mEdgeGlowBottom.onAbsorb((int) mScroller.getCurrVelocity());
14451fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    }
14461fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                }
14471fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
14481fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
14491fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
14501fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
14511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
14521fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * Scrolls the view to the given child.
14531fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *
14541fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @param child the View to scroll to
14551fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
14561fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private void scrollToChild(View child) {
14571fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        child.getDrawingRect(mTempRect);
14581fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
14591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        /* Offset from child's local coordinates to ScrollView coordinates */
14601fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        offsetDescendantRectToMyCoords(child, mTempRect);
14611fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
14621fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        int scrollDelta = computeScrollDeltaToGetChildRectOnScreen(mTempRect);
14631fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
14641fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (scrollDelta != 0) {
14651fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            scrollBy(0, scrollDelta);
14661fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
14671fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
14681fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
14691fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
14701fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * If rect is off screen, scroll just enough to get it (or at least the
14711fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * first screen size chunk of it) on screen.
14721fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *
14731fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @param rect      The rectangle.
14741fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @param immediate True to scroll immediately without animation
14751fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @return true if scrolling was performed
14761fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
14771fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private boolean scrollToChildRect(Rect rect, boolean immediate) {
14781fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        final int delta = computeScrollDeltaToGetChildRectOnScreen(rect);
14791fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        final boolean scroll = delta != 0;
14801fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (scroll) {
14811fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            if (immediate) {
14821fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                scrollBy(0, delta);
14831fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            } else {
14841fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                smoothScrollBy(0, delta);
14851fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
14861fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
14871fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return scroll;
14881fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
14891fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
14901fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
14911fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * Compute the amount to scroll in the Y direction in order to get
14921fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * a rectangle completely on the screen (or, if taller than the screen,
14931fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * at least the first screen size chunk of it).
14941fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *
14951fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @param rect The rect.
14961fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @return The scroll delta.
14971fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
14981fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    protected int computeScrollDeltaToGetChildRectOnScreen(Rect rect) {
14991fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (getChildCount() == 0) return 0;
15001fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
15011fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        int height = getHeight();
15021fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        int screenTop = getScrollY();
15031fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        int screenBottom = screenTop + height;
15041fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
15051fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        int fadingEdge = getVerticalFadingEdgeLength();
15061fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
15071fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        // leave room for top fading edge as long as rect isn't at very top
15081fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (rect.top > 0) {
15091fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            screenTop += fadingEdge;
15101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
15111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
15121fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        // leave room for bottom fading edge as long as rect isn't at very bottom
15131fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (rect.bottom < getChildAt(0).getHeight()) {
15141fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            screenBottom -= fadingEdge;
15151fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
15161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
15171fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        int scrollYDelta = 0;
15181fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
15191fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (rect.bottom > screenBottom && rect.top > screenTop) {
15201fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            // need to move down to get it in view: move down just enough so
15211fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            // that the entire rectangle is in view (or at least the first
15221fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            // screen size chunk).
15231fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
15241fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            if (rect.height() > height) {
15251fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                // just enough to get screen size chunk on
15261fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                scrollYDelta += (rect.top - screenTop);
15271fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            } else {
15281fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                // get entire rect at bottom of screen
15291fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                scrollYDelta += (rect.bottom - screenBottom);
15301fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
15311fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
15321fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            // make sure we aren't scrolling beyond the end of our content
15331fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            int bottom = getChildAt(0).getBottom();
15341fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            int distanceToBottom = bottom - screenBottom;
15351fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            scrollYDelta = Math.min(scrollYDelta, distanceToBottom);
15361fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
15371fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        } else if (rect.top < screenTop && rect.bottom < screenBottom) {
15381fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            // need to move up to get it in view: move up just enough so that
15391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            // entire rectangle is in view (or at least the first screen
15401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            // size chunk of it).
15411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
15421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            if (rect.height() > height) {
15431fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                // screen size chunk
15441fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                scrollYDelta -= (screenBottom - rect.bottom);
15451fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            } else {
15461fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                // entire rect at top
15471fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                scrollYDelta -= (screenTop - rect.top);
15481fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
15491fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
15501fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            // make sure we aren't scrolling any further than the top our content
15511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            scrollYDelta = Math.max(scrollYDelta, -getScrollY());
15521fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
15531fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return scrollYDelta;
15541fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
15551fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
15561fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
15571fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public void requestChildFocus(View child, View focused) {
15581fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (!mIsLayoutDirty) {
15591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            scrollToChild(focused);
15601fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        } else {
15611fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            // The child may not be laid out yet, we can't compute the scroll yet
15621fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            mChildToScrollTo = focused;
15631fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
15641fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        super.requestChildFocus(child, focused);
15651fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
15661fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
15671fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
15681fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
15691fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * When looking for focus in children of a scroll view, need to be a little
15701fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * more careful not to give focus to something that is scrolled off screen.
15711fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *
15721fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * This is more expensive than the default {@link android.view.ViewGroup}
15731fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * implementation, otherwise this behavior might have been made the default.
15741fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
15751fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
15761fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    protected boolean onRequestFocusInDescendants(int direction,
15771fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            Rect previouslyFocusedRect) {
15781fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
15791fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        // convert from forward / backward notation to up / down / left / right
15801fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        // (ugh).
15811fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (direction == View.FOCUS_FORWARD) {
15821fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            direction = View.FOCUS_DOWN;
15831fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        } else if (direction == View.FOCUS_BACKWARD) {
15841fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            direction = View.FOCUS_UP;
15851fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
15861fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
15874d2c7b7c4f194034c5f17c4bee7320d808aabe4cAurimas Liutikas        final View nextFocus = previouslyFocusedRect == null
15884d2c7b7c4f194034c5f17c4bee7320d808aabe4cAurimas Liutikas                ? FocusFinder.getInstance().findNextFocus(this, null, direction)
15894d2c7b7c4f194034c5f17c4bee7320d808aabe4cAurimas Liutikas                : FocusFinder.getInstance().findNextFocusFromRect(
15904d2c7b7c4f194034c5f17c4bee7320d808aabe4cAurimas Liutikas                        this, previouslyFocusedRect, direction);
15911fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
15921fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (nextFocus == null) {
15931fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            return false;
15941fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
15951fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
15961fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (isOffScreen(nextFocus)) {
15971fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            return false;
15981fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
15991fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
16001fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return nextFocus.requestFocus(direction, previouslyFocusedRect);
16011fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
16021fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
16031fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
16041fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public boolean requestChildRectangleOnScreen(View child, Rect rectangle,
16051fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            boolean immediate) {
16061fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        // offset into coordinate space of this scroll view
16071fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        rectangle.offset(child.getLeft() - child.getScrollX(),
16081fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                child.getTop() - child.getScrollY());
16091fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
16101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return scrollToChildRect(rectangle, immediate);
16111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
16121fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
16131fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
16141fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public void requestLayout() {
16151fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        mIsLayoutDirty = true;
16161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        super.requestLayout();
16171fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
16181fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
16191fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
16201fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    protected void onLayout(boolean changed, int l, int t, int r, int b) {
16211fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        super.onLayout(changed, l, t, r, b);
16221fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        mIsLayoutDirty = false;
16231fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        // Give a child focus if it needs it
16241fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (mChildToScrollTo != null && isViewDescendantOf(mChildToScrollTo, this)) {
16251fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            scrollToChild(mChildToScrollTo);
16261fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
16271fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        mChildToScrollTo = null;
16281fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
16291fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (!mIsLaidOut) {
16301fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            if (mSavedState != null) {
16311fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                scrollTo(getScrollX(), mSavedState.scrollPosition);
16321fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                mSavedState = null;
16331fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            } // mScrollY default value is "0"
16341fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
16351fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            final int childHeight = (getChildCount() > 0) ? getChildAt(0).getMeasuredHeight() : 0;
16361fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            final int scrollRange = Math.max(0,
16371fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    childHeight - (b - t - getPaddingBottom() - getPaddingTop()));
16381fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
16391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            // Don't forget to clamp
16401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            if (getScrollY() > scrollRange) {
16411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                scrollTo(getScrollX(), scrollRange);
16421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            } else if (getScrollY() < 0) {
16431fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                scrollTo(getScrollX(), 0);
16441fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
16451fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
16461fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
16471fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        // Calling this with the present values causes it to re-claim them
16481fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        scrollTo(getScrollX(), getScrollY());
16491fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        mIsLaidOut = true;
16501fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
16511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
16521fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
16531fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public void onAttachedToWindow() {
1654d344e81ad956ec445038f5ba2ca2ebd1831b1f8aAlan Viverette        super.onAttachedToWindow();
1655d344e81ad956ec445038f5ba2ca2ebd1831b1f8aAlan Viverette
16561fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        mIsLaidOut = false;
16571fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
16581fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
16591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
16601fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
16611fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        super.onSizeChanged(w, h, oldw, oldh);
16621fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
16631fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        View currentFocused = findFocus();
16644d2c7b7c4f194034c5f17c4bee7320d808aabe4cAurimas Liutikas        if (null == currentFocused || this == currentFocused) {
16651fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            return;
16664d2c7b7c4f194034c5f17c4bee7320d808aabe4cAurimas Liutikas        }
16671fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
16681fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        // If the currently-focused view was visible on the screen when the
16691fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        // screen was at the old height, then scroll the screen to make that
16701fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        // view visible with the new screen height.
16711fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (isWithinDeltaOfScreen(currentFocused, 0, oldh)) {
16721fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            currentFocused.getDrawingRect(mTempRect);
16731fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            offsetDescendantRectToMyCoords(currentFocused, mTempRect);
16741fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            int scrollDelta = computeScrollDeltaToGetChildRectOnScreen(mTempRect);
16751fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            doScrollY(scrollDelta);
16761fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
16771fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
16781fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
16791fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
16801fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * Return true if child is a descendant of parent, (or equal to the parent).
16811fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
16821fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private static boolean isViewDescendantOf(View child, View parent) {
16831fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (child == parent) {
16841fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            return true;
16851fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
16861fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
16871fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        final ViewParent theParent = child.getParent();
16881fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return (theParent instanceof ViewGroup) && isViewDescendantOf((View) theParent, parent);
16891fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
16901fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
16911fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
16921fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * Fling the scroll view
16931fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *
16941fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @param velocityY The initial velocity in the Y direction. Positive
16951fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *                  numbers mean that the finger/cursor is moving down the screen,
16961fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *                  which means we want to scroll towards the top.
16971fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
16981fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public void fling(int velocityY) {
16991fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (getChildCount() > 0) {
17001fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            int height = getHeight() - getPaddingBottom() - getPaddingTop();
17011fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            int bottom = getChildAt(0).getHeight();
17021fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
17031fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            mScroller.fling(getScrollX(), getScrollY(), 0, velocityY, 0, 0, 0,
17044d2c7b7c4f194034c5f17c4bee7320d808aabe4cAurimas Liutikas                    Math.max(0, bottom - height), 0, height / 2);
17051fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
17061fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            ViewCompat.postInvalidateOnAnimation(this);
17071fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
17081fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
17091fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
17101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private void flingWithNestedDispatch(int velocityY) {
17111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        final int scrollY = getScrollY();
17124d2c7b7c4f194034c5f17c4bee7320d808aabe4cAurimas Liutikas        final boolean canFling = (scrollY > 0 || velocityY > 0)
17134d2c7b7c4f194034c5f17c4bee7320d808aabe4cAurimas Liutikas                && (scrollY < getScrollRange() || velocityY < 0);
17141fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (!dispatchNestedPreFling(0, velocityY)) {
17151fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            dispatchNestedFling(0, velocityY, canFling);
17161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            if (canFling) {
17171fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                fling(velocityY);
17181fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
17191fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
17201fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
17211fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
17221fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private void endDrag() {
17231fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        mIsBeingDragged = false;
17241fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
17251fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        recycleVelocityTracker();
1726d4c72f08279df548fbc4d556fe1a8b3500fafdbcChris Banes        stopNestedScroll();
17271fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
17281fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (mEdgeGlowTop != null) {
17291fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            mEdgeGlowTop.onRelease();
17301fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            mEdgeGlowBottom.onRelease();
17311fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
17321fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
17331fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
17341fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
17351fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * {@inheritDoc}
17361fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *
17371fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * <p>This version also clamps the scrolling to the bounds of our child.
17381fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
17391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
17401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public void scrollTo(int x, int y) {
17411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        // we rely on the fact the View.scrollBy calls scrollTo.
17421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (getChildCount() > 0) {
17431fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            View child = getChildAt(0);
17441fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            x = clamp(x, getWidth() - getPaddingRight() - getPaddingLeft(), child.getWidth());
17451fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            y = clamp(y, getHeight() - getPaddingBottom() - getPaddingTop(), child.getHeight());
17461fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            if (x != getScrollX() || y != getScrollY()) {
17471fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                super.scrollTo(x, y);
17481fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
17491fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
17501fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
17511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
17521fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private void ensureGlows() {
1753e12dfa03641ad9cf0ddf272675bbe7d1198adbfdAurimas Liutikas        if (getOverScrollMode() != View.OVER_SCROLL_NEVER) {
17541fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            if (mEdgeGlowTop == null) {
17551fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                Context context = getContext();
1756506695b4cf6b22d70d2e3647d94551e721ee0430Aurimas Liutikas                mEdgeGlowTop = new EdgeEffect(context);
1757506695b4cf6b22d70d2e3647d94551e721ee0430Aurimas Liutikas                mEdgeGlowBottom = new EdgeEffect(context);
17581fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
17591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        } else {
17601fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            mEdgeGlowTop = null;
17611fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            mEdgeGlowBottom = null;
17621fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
17631fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
17641fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
17651fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
17661fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public void draw(Canvas canvas) {
17671fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        super.draw(canvas);
17681fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (mEdgeGlowTop != null) {
17691fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            final int scrollY = getScrollY();
17701fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            if (!mEdgeGlowTop.isFinished()) {
17711fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                final int restoreCount = canvas.save();
17721fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                final int width = getWidth() - getPaddingLeft() - getPaddingRight();
17731fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
17741fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                canvas.translate(getPaddingLeft(), Math.min(0, scrollY));
17751fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                mEdgeGlowTop.setSize(width, getHeight());
17761fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                if (mEdgeGlowTop.draw(canvas)) {
17771fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    ViewCompat.postInvalidateOnAnimation(this);
17781fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                }
17791fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                canvas.restoreToCount(restoreCount);
17801fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
17811fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            if (!mEdgeGlowBottom.isFinished()) {
17821fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                final int restoreCount = canvas.save();
17831fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                final int width = getWidth() - getPaddingLeft() - getPaddingRight();
17841fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                final int height = getHeight();
17851fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
17861fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                canvas.translate(-width + getPaddingLeft(),
17871fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        Math.max(getScrollRange(), scrollY) + height);
17881fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                canvas.rotate(180, width, 0);
17891fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                mEdgeGlowBottom.setSize(width, height);
17901fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                if (mEdgeGlowBottom.draw(canvas)) {
17911fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    ViewCompat.postInvalidateOnAnimation(this);
17921fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                }
17931fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                canvas.restoreToCount(restoreCount);
17941fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
17951fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
17961fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
17971fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
17981fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private static int clamp(int n, int my, int child) {
17991fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (my >= child || n < 0) {
18001fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            /* my >= child is this case:
18011fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell             *                    |--------------- me ---------------|
18021fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell             *     |------ child ------|
18031fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell             * or
18041fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell             *     |--------------- me ---------------|
18051fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell             *            |------ child ------|
18061fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell             * or
18071fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell             *     |--------------- me ---------------|
18081fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell             *                                  |------ child ------|
18091fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell             *
18101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell             * n < 0 is this case:
18111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell             *     |------ me ------|
18121fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell             *                    |-------- child --------|
18131fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell             *     |-- mScrollX --|
18141fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell             */
18151fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            return 0;
18161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
18174d2c7b7c4f194034c5f17c4bee7320d808aabe4cAurimas Liutikas        if ((my + n) > child) {
18181fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            /* this case:
18191fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell             *                    |------ me ------|
18201fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell             *     |------ child ------|
18211fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell             *     |-- mScrollX --|
18221fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell             */
18234d2c7b7c4f194034c5f17c4bee7320d808aabe4cAurimas Liutikas            return child - my;
18241fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
18251fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return n;
18261fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
18271fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
18281fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
18291fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    protected void onRestoreInstanceState(Parcelable state) {
1830bb58a82daf11bf3c056d1cd5887aa26435d37b69Aurimas Liutikas        if (!(state instanceof SavedState)) {
1831bb58a82daf11bf3c056d1cd5887aa26435d37b69Aurimas Liutikas            super.onRestoreInstanceState(state);
1832bb58a82daf11bf3c056d1cd5887aa26435d37b69Aurimas Liutikas            return;
1833bb58a82daf11bf3c056d1cd5887aa26435d37b69Aurimas Liutikas        }
1834bb58a82daf11bf3c056d1cd5887aa26435d37b69Aurimas Liutikas
18351fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        SavedState ss = (SavedState) state;
18361fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        super.onRestoreInstanceState(ss.getSuperState());
18371fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        mSavedState = ss;
18381fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        requestLayout();
18391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
18401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
18411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
18421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    protected Parcelable onSaveInstanceState() {
18431fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        Parcelable superState = super.onSaveInstanceState();
18441fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        SavedState ss = new SavedState(superState);
18451fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        ss.scrollPosition = getScrollY();
18461fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return ss;
18471fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
18481fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
18491fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    static class SavedState extends BaseSavedState {
18501fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        public int scrollPosition;
18511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
18521fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        SavedState(Parcelable superState) {
18531fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            super(superState);
18541fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
18551fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
18564d2c7b7c4f194034c5f17c4bee7320d808aabe4cAurimas Liutikas        SavedState(Parcel source) {
18571fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            super(source);
18581fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            scrollPosition = source.readInt();
18591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
18601fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
18611fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        @Override
18621fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        public void writeToParcel(Parcel dest, int flags) {
18631fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            super.writeToParcel(dest, flags);
18641fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            dest.writeInt(scrollPosition);
18651fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
18661fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
18671fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        @Override
18681fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        public String toString() {
18691fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            return "HorizontalScrollView.SavedState{"
18701fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    + Integer.toHexString(System.identityHashCode(this))
18711fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    + " scrollPosition=" + scrollPosition + "}";
18721fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
18731fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
18744d2c7b7c4f194034c5f17c4bee7320d808aabe4cAurimas Liutikas        public static final Parcelable.Creator<SavedState> CREATOR =
18754d2c7b7c4f194034c5f17c4bee7320d808aabe4cAurimas Liutikas                new Parcelable.Creator<SavedState>() {
187615375aa6fd54b036f97f99229aefab2822c8a1c9Aurimas Liutikas            @Override
18771fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            public SavedState createFromParcel(Parcel in) {
18781fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                return new SavedState(in);
18791fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
18801fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
188115375aa6fd54b036f97f99229aefab2822c8a1c9Aurimas Liutikas            @Override
18821fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            public SavedState[] newArray(int size) {
18831fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                return new SavedState[size];
18841fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
18851fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        };
18861fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
18871fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
18881fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    static class AccessibilityDelegate extends AccessibilityDelegateCompat {
18891fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        @Override
18901fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        public boolean performAccessibilityAction(View host, int action, Bundle arguments) {
18911fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            if (super.performAccessibilityAction(host, action, arguments)) {
18921fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                return true;
18931fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
18941fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            final NestedScrollView nsvHost = (NestedScrollView) host;
18951fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            if (!nsvHost.isEnabled()) {
18961fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                return false;
18971fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
18981fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            switch (action) {
18991fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                case AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD: {
19001fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    final int viewportHeight = nsvHost.getHeight() - nsvHost.getPaddingBottom()
19011fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                            - nsvHost.getPaddingTop();
19021fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    final int targetScrollY = Math.min(nsvHost.getScrollY() + viewportHeight,
19031fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                            nsvHost.getScrollRange());
19041fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    if (targetScrollY != nsvHost.getScrollY()) {
19051fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        nsvHost.smoothScrollTo(0, targetScrollY);
19061fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        return true;
19071fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    }
19081fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                }
19091fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                return false;
19101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                case AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD: {
19111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    final int viewportHeight = nsvHost.getHeight() - nsvHost.getPaddingBottom()
19121fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                            - nsvHost.getPaddingTop();
19131fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    final int targetScrollY = Math.max(nsvHost.getScrollY() - viewportHeight, 0);
19141fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    if (targetScrollY != nsvHost.getScrollY()) {
19151fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        nsvHost.smoothScrollTo(0, targetScrollY);
19161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        return true;
19171fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    }
19181fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                }
19191fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                return false;
19201fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
19211fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            return false;
19221fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
19231fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
19241fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        @Override
19251fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) {
19261fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            super.onInitializeAccessibilityNodeInfo(host, info);
19271fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            final NestedScrollView nsvHost = (NestedScrollView) host;
19281fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            info.setClassName(ScrollView.class.getName());
19291fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            if (nsvHost.isEnabled()) {
19301fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                final int scrollRange = nsvHost.getScrollRange();
19311fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                if (scrollRange > 0) {
19321fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    info.setScrollable(true);
19331fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    if (nsvHost.getScrollY() > 0) {
19341fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD);
19351fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    }
19361fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    if (nsvHost.getScrollY() < scrollRange) {
19371fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD);
19381fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    }
19391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                }
19401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
19411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
19421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
19431fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        @Override
19441fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) {
19451fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            super.onInitializeAccessibilityEvent(host, event);
19461fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            final NestedScrollView nsvHost = (NestedScrollView) host;
19471fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            event.setClassName(ScrollView.class.getName());
19481fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            final AccessibilityRecordCompat record = AccessibilityEventCompat.asRecord(event);
19491fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            final boolean scrollable = nsvHost.getScrollRange() > 0;
19501fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            record.setScrollable(scrollable);
19511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            record.setScrollX(nsvHost.getScrollX());
19521fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            record.setScrollY(nsvHost.getScrollY());
19531fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            record.setMaxScrollX(nsvHost.getScrollX());
19541fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            record.setMaxScrollY(nsvHost.getScrollRange());
19551fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
19561fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
19571fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell}
1958