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
18ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikaspackage androidx.core.widget;
191fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
20ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport static androidx.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;
26bcb5323316d898cc22873efc2a145a5912e8a75cshepshapardimport android.os.Build;
271fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.os.Bundle;
281fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.os.Parcel;
291fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.os.Parcelable;
301fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.util.AttributeSet;
311fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.util.Log;
321fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.util.TypedValue;
331fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.view.FocusFinder;
341fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.view.KeyEvent;
351fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.view.MotionEvent;
361fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.view.VelocityTracker;
371fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.view.View;
381fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.view.ViewConfiguration;
391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.view.ViewGroup;
401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.view.ViewParent;
411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.view.accessibility.AccessibilityEvent;
421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.view.animation.AnimationUtils;
43506695b4cf6b22d70d2e3647d94551e721ee0430Aurimas Liutikasimport android.widget.EdgeEffect;
441fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.widget.FrameLayout;
453035be16658d7652fdc472f971c81d8f7ffb60fdAurimas Liutikasimport android.widget.OverScroller;
461fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.widget.ScrollView;
471fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
489dede51868bbbe16aadcd65e04860bea8ea50e05Aurimas Liutikasimport androidx.annotation.NonNull;
499dede51868bbbe16aadcd65e04860bea8ea50e05Aurimas Liutikasimport androidx.annotation.Nullable;
509dede51868bbbe16aadcd65e04860bea8ea50e05Aurimas Liutikasimport androidx.annotation.RestrictTo;
519dede51868bbbe16aadcd65e04860bea8ea50e05Aurimas Liutikasimport androidx.core.view.AccessibilityDelegateCompat;
529dede51868bbbe16aadcd65e04860bea8ea50e05Aurimas Liutikasimport androidx.core.view.InputDeviceCompat;
539dede51868bbbe16aadcd65e04860bea8ea50e05Aurimas Liutikasimport androidx.core.view.NestedScrollingChild2;
549dede51868bbbe16aadcd65e04860bea8ea50e05Aurimas Liutikasimport androidx.core.view.NestedScrollingChildHelper;
55bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapardimport androidx.core.view.NestedScrollingParent2;
569dede51868bbbe16aadcd65e04860bea8ea50e05Aurimas Liutikasimport androidx.core.view.NestedScrollingParentHelper;
579dede51868bbbe16aadcd65e04860bea8ea50e05Aurimas Liutikasimport androidx.core.view.ScrollingView;
589dede51868bbbe16aadcd65e04860bea8ea50e05Aurimas Liutikasimport androidx.core.view.ViewCompat;
599dede51868bbbe16aadcd65e04860bea8ea50e05Aurimas Liutikasimport androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
609dede51868bbbe16aadcd65e04860bea8ea50e05Aurimas Liutikasimport androidx.core.view.accessibility.AccessibilityRecordCompat;
619dede51868bbbe16aadcd65e04860bea8ea50e05Aurimas Liutikas
621fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport java.util.List;
631fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
641fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell/**
651fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * NestedScrollView is just like {@link android.widget.ScrollView}, but it supports acting
661fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * as both a nested scrolling parent and child on both new and old versions of Android.
671fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * Nested scrolling is enabled by default.
681fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */
69bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapardpublic class NestedScrollView extends FrameLayout implements NestedScrollingParent2,
7076daed103193a1756535d1f59b165e98e1d17445Chris Banes        NestedScrollingChild2, ScrollingView {
711fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    static final int ANIMATED_SCROLL_GAP = 250;
721fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
731fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    static final float MAX_SCROLL_FACTOR = 0.5f;
741fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
751fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private static final String TAG = "NestedScrollView";
761fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
77cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes    /**
78cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes     * Interface definition for a callback to be invoked when the scroll
79cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes     * X or Y positions of a view change.
80cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes     *
81cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes     * <p>This version of the interface works on all versions of Android, back to API v4.</p>
82cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes     *
83cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes     * @see #setOnScrollChangeListener(OnScrollChangeListener)
84cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes     */
85cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes    public interface OnScrollChangeListener {
86cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes        /**
87cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes         * Called when the scroll position of a view changes.
88cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes         *
89cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes         * @param v The view whose scroll position has changed.
90cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes         * @param scrollX Current horizontal scroll origin.
91cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes         * @param scrollY Current vertical scroll origin.
92cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes         * @param oldScrollX Previous horizontal scroll origin.
93cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes         * @param oldScrollY Previous vertical scroll origin.
94cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes         */
95cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes        void onScrollChange(NestedScrollView v, int scrollX, int scrollY,
96cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes                int oldScrollX, int oldScrollY);
97cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes    }
98cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes
991fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private long mLastScroll;
1001fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
1011fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private final Rect mTempRect = new Rect();
1023035be16658d7652fdc472f971c81d8f7ffb60fdAurimas Liutikas    private OverScroller mScroller;
103506695b4cf6b22d70d2e3647d94551e721ee0430Aurimas Liutikas    private EdgeEffect mEdgeGlowTop;
104506695b4cf6b22d70d2e3647d94551e721ee0430Aurimas Liutikas    private EdgeEffect mEdgeGlowBottom;
1051fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
1061fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
1071fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * Position of the last motion event.
1081fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
1091fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private int mLastMotionY;
1101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
1111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
1121fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * True when the layout has changed but the traversal has not come through yet.
1131fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * Ideally the view hierarchy would keep track of this for us.
1141fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
1151fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private boolean mIsLayoutDirty = true;
1161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private boolean mIsLaidOut = false;
1171fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
1181fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
1191fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * The child to give focus to in the event that a child has requested focus while the
1201fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * layout is dirty. This prevents the scroll from being wrong if the child has not been
1211fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * laid out before requesting focus.
1221fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
1231fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private View mChildToScrollTo = null;
1241fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
1251fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
1261fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * True if the user is currently dragging this ScrollView around. This is
1271fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * not the same as 'is being flinged', which can be checked by
1281fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * mScroller.isFinished() (flinging begins when the user lifts his finger).
1291fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
1301fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private boolean mIsBeingDragged = false;
1311fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
1321fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
1331fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * Determines speed during touch scrolling
1341fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
1351fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private VelocityTracker mVelocityTracker;
1361fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
1371fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
1381fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * When set to true, the scroll view measure its child to make it fill the currently
1391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * visible area.
1401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
1411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private boolean mFillViewport;
1421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
1431fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
1441fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * Whether arrow scrolling is animated.
1451fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
1461fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private boolean mSmoothScrollingEnabled = true;
1471fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
1481fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private int mTouchSlop;
1491fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private int mMinimumVelocity;
1501fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private int mMaximumVelocity;
1511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
1521fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
1531fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * ID of the active pointer. This is used to retain consistency during
1541fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * drags/flings if multiple pointers are used.
1551fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
1561fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private int mActivePointerId = INVALID_POINTER;
1571fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
1581fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
1591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * Used during scrolling to retrieve the new offset within the window.
1601fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
1611fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private final int[] mScrollOffset = new int[2];
1621fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private final int[] mScrollConsumed = new int[2];
1631fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private int mNestedYOffset;
1641fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
16576daed103193a1756535d1f59b165e98e1d17445Chris Banes    private int mLastScrollerY;
16676daed103193a1756535d1f59b165e98e1d17445Chris Banes
1671fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
1681fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * Sentinel value for no current active pointer.
1691fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * Used by {@link #mActivePointerId}.
1701fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
1711fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private static final int INVALID_POINTER = -1;
1721fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
1731fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private SavedState mSavedState;
1741fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
1751fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private static final AccessibilityDelegate ACCESSIBILITY_DELEGATE = new AccessibilityDelegate();
1761fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
1771fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private static final int[] SCROLLVIEW_STYLEABLE = new int[] {
1781fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            android.R.attr.fillViewport
1791fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    };
1801fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
1811fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private final NestedScrollingParentHelper mParentHelper;
1821fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private final NestedScrollingChildHelper mChildHelper;
1831fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
1841fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private float mVerticalScrollFactor;
1851fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
186cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes    private OnScrollChangeListener mOnScrollChangeListener;
187cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes
188852749684a10e7d104eed8fb2dcc68b64d079340Jake Wharton    public NestedScrollView(@NonNull Context context) {
1891fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        this(context, null);
1901fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
1911fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
192852749684a10e7d104eed8fb2dcc68b64d079340Jake Wharton    public NestedScrollView(@NonNull Context context, @Nullable AttributeSet attrs) {
1931fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        this(context, attrs, 0);
1941fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
1951fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
196852749684a10e7d104eed8fb2dcc68b64d079340Jake Wharton    public NestedScrollView(@NonNull Context context, @Nullable AttributeSet attrs,
197852749684a10e7d104eed8fb2dcc68b64d079340Jake Wharton            int defStyleAttr) {
1981fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        super(context, attrs, defStyleAttr);
1991fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        initScrollView();
2001fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
2011fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        final TypedArray a = context.obtainStyledAttributes(
2021fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                attrs, SCROLLVIEW_STYLEABLE, defStyleAttr, 0);
2031fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
2041fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        setFillViewport(a.getBoolean(0, false));
2051fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
2061fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        a.recycle();
2071fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
2081fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        mParentHelper = new NestedScrollingParentHelper(this);
2091fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        mChildHelper = new NestedScrollingChildHelper(this);
2101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
2111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        // ...because why else would you be using this widget?
2121fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        setNestedScrollingEnabled(true);
2131fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
2141fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        ViewCompat.setAccessibilityDelegate(this, ACCESSIBILITY_DELEGATE);
2151fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
2161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
217bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard    // NestedScrollingChild2
218bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard
219bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard    @Override
220bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard    public boolean startNestedScroll(int axes, int type) {
221bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard        return mChildHelper.startNestedScroll(axes, type);
222bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard    }
223bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard
224bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard    @Override
225bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard    public void stopNestedScroll(int type) {
226bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard        mChildHelper.stopNestedScroll(type);
227bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard    }
228bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard
229bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard    @Override
230bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard    public boolean hasNestedScrollingParent(int type) {
231bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard        return mChildHelper.hasNestedScrollingParent(type);
232bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard    }
233bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard
234bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard    @Override
235bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard    public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed,
236bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard            int dyUnconsumed, int[] offsetInWindow, int type) {
237bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard        return mChildHelper.dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed,
238bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard                offsetInWindow, type);
239bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard    }
240bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard
241bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard    @Override
242bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard    public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow,
243bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard            int type) {
244bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard        return mChildHelper.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow, type);
245bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard    }
246bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard
2471fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    // NestedScrollingChild
2481fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
2491fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
2501fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public void setNestedScrollingEnabled(boolean enabled) {
2511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        mChildHelper.setNestedScrollingEnabled(enabled);
2521fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
2531fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
2541fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
2551fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public boolean isNestedScrollingEnabled() {
2561fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return mChildHelper.isNestedScrollingEnabled();
2571fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
2581fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
2591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
2601fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public boolean startNestedScroll(int axes) {
261bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard        return startNestedScroll(axes, ViewCompat.TYPE_TOUCH);
2621fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
2631fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
2641fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
265bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard    public void stopNestedScroll() {
266bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard        stopNestedScroll(ViewCompat.TYPE_TOUCH);
26776daed103193a1756535d1f59b165e98e1d17445Chris Banes    }
26876daed103193a1756535d1f59b165e98e1d17445Chris Banes
26976daed103193a1756535d1f59b165e98e1d17445Chris Banes    @Override
270bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard    public boolean hasNestedScrollingParent() {
271bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard        return hasNestedScrollingParent(ViewCompat.TYPE_TOUCH);
2721fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
2731fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
2741fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
275bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard    public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed,
276bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard            int dyUnconsumed, int[] offsetInWindow) {
277bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard        return dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed,
278bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard                offsetInWindow, ViewCompat.TYPE_TOUCH);
27976daed103193a1756535d1f59b165e98e1d17445Chris Banes    }
28076daed103193a1756535d1f59b165e98e1d17445Chris Banes
28176daed103193a1756535d1f59b165e98e1d17445Chris Banes    @Override
282bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard    public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) {
283bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard        return dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow, ViewCompat.TYPE_TOUCH);
2841fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
2851fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
2861fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
287bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard    public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) {
288bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard        return mChildHelper.dispatchNestedFling(velocityX, velocityY, consumed);
28976daed103193a1756535d1f59b165e98e1d17445Chris Banes    }
29076daed103193a1756535d1f59b165e98e1d17445Chris Banes
29176daed103193a1756535d1f59b165e98e1d17445Chris Banes    @Override
292bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard    public boolean dispatchNestedPreFling(float velocityX, float velocityY) {
293bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard        return mChildHelper.dispatchNestedPreFling(velocityX, velocityY);
2941fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
2951fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
296bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard    // NestedScrollingParent2
297bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard
2981fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
299bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard    public boolean onStartNestedScroll(@NonNull View child, @NonNull View target, int axes,
300bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard            int type) {
301bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard        return (axes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0;
30276daed103193a1756535d1f59b165e98e1d17445Chris Banes    }
30376daed103193a1756535d1f59b165e98e1d17445Chris Banes
30476daed103193a1756535d1f59b165e98e1d17445Chris Banes    @Override
305bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard    public void onNestedScrollAccepted(@NonNull View child, @NonNull View target, int axes,
306bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard            int type) {
307bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard        mParentHelper.onNestedScrollAccepted(child, target, axes, type);
308bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard        startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL, type);
3091fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
3101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
3111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
312bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard    public void onStopNestedScroll(@NonNull View target, int type) {
313bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard        mParentHelper.onStopNestedScroll(target, type);
314bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard        stopNestedScroll(type);
31576daed103193a1756535d1f59b165e98e1d17445Chris Banes    }
31676daed103193a1756535d1f59b165e98e1d17445Chris Banes
31776daed103193a1756535d1f59b165e98e1d17445Chris Banes    @Override
318bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard    public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed,
319bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard            int dyUnconsumed, int type) {
320bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard        final int oldScrollY = getScrollY();
321bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard        scrollBy(0, dyUnconsumed);
322bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard        final int myConsumed = getScrollY() - oldScrollY;
323bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard        final int myUnconsumed = dyUnconsumed - myConsumed;
324bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard        dispatchNestedScroll(0, myConsumed, 0, myUnconsumed, null,
325bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard                type);
3261fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
3271fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
3281fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
329bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard    public void onNestedPreScroll(@NonNull View target, int dx, int dy, @NonNull int[] consumed,
330bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard            int type) {
331bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard        dispatchNestedPreScroll(dx, dy, consumed, null, type);
3321fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
3331fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
3341fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    // NestedScrollingParent
3351fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
3361fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
3371fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) {
338bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard        return onStartNestedScroll(child, target, nestedScrollAxes, ViewCompat.TYPE_TOUCH);
3391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
3401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
3411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
3421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public void onNestedScrollAccepted(View child, View target, int nestedScrollAxes) {
343bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard        onNestedScrollAccepted(child, target, nestedScrollAxes, ViewCompat.TYPE_TOUCH);
3441fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
3451fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
3461fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
3471fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public void onStopNestedScroll(View target) {
348bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard        onStopNestedScroll(target, ViewCompat.TYPE_TOUCH);
3491fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
3501fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
3511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
3521fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed,
3531fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            int dyUnconsumed) {
354bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard        onNestedScroll(target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed,
355bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard                ViewCompat.TYPE_TOUCH);
3561fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
3571fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
3581fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
3591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) {
360bd9c35d6db58b5c9b075d751a2c64203eccc6cd7shepshapard        onNestedPreScroll(target, dx, dy, consumed, ViewCompat.TYPE_TOUCH);
3611fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
3621fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
3631fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
3641fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) {
3651fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (!consumed) {
3661fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            flingWithNestedDispatch((int) velocityY);
3671fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            return true;
3681fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
3691fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return false;
3701fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
3711fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
3721fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
3731fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public boolean onNestedPreFling(View target, float velocityX, float velocityY) {
374d370f75e50bf31d348d4e424ba8477ac77635f70Chris Banes        return dispatchNestedPreFling(velocityX, velocityY);
3751fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
3761fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
3771fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
3781fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public int getNestedScrollAxes() {
3791fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return mParentHelper.getNestedScrollAxes();
3801fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
3811fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
3821fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    // ScrollView import
3831fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
384e2104f4b5c8e3ad63570306a25e61502dfe4c418Aurimas Liutikas    @Override
3851fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public boolean shouldDelayChildPressedState() {
3861fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return true;
3871fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
3881fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
3891fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
3901fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    protected float getTopFadingEdgeStrength() {
3911fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (getChildCount() == 0) {
3921fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            return 0.0f;
3931fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
3941fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
3951fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        final int length = getVerticalFadingEdgeLength();
3961fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        final int scrollY = getScrollY();
3971fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (scrollY < length) {
3981fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            return scrollY / (float) length;
3991fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
4001fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
4011fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return 1.0f;
4021fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
4031fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
4041fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
4051fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    protected float getBottomFadingEdgeStrength() {
4061fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (getChildCount() == 0) {
4071fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            return 0.0f;
4081fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
4091fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
41093ade4a86ccf8e87af43401a3b3e41a804e311a1shepshapard        View child = getChildAt(0);
41193ade4a86ccf8e87af43401a3b3e41a804e311a1shepshapard        final NestedScrollView.LayoutParams lp = (LayoutParams) child.getLayoutParams();
4121fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        final int length = getVerticalFadingEdgeLength();
4131fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        final int bottomEdge = getHeight() - getPaddingBottom();
41493ade4a86ccf8e87af43401a3b3e41a804e311a1shepshapard        final int span = child.getBottom() + lp.bottomMargin - getScrollY() - bottomEdge;
4151fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (span < length) {
4161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            return span / (float) length;
4171fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
4181fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
4191fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return 1.0f;
4201fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
4211fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
4221fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
4231fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @return The maximum amount this scroll view will scroll in response to
4241fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *   an arrow event.
4251fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
4261fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public int getMaxScrollAmount() {
4271fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return (int) (MAX_SCROLL_FACTOR * getHeight());
4281fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
4291fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
4301fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private void initScrollView() {
4313035be16658d7652fdc472f971c81d8f7ffb60fdAurimas Liutikas        mScroller = new OverScroller(getContext());
4321fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        setFocusable(true);
4331fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
4341fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        setWillNotDraw(false);
4351fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        final ViewConfiguration configuration = ViewConfiguration.get(getContext());
4361fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        mTouchSlop = configuration.getScaledTouchSlop();
4371fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
4381fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
4391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
4401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
4411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
4421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public void addView(View child) {
4431fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (getChildCount() > 0) {
4441fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            throw new IllegalStateException("ScrollView can host only one direct child");
4451fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
4461fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
4471fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        super.addView(child);
4481fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
4491fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
4501fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
4511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public void addView(View child, int index) {
4521fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (getChildCount() > 0) {
4531fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            throw new IllegalStateException("ScrollView can host only one direct child");
4541fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
4551fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
4561fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        super.addView(child, index);
4571fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
4581fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
4591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
4601fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public void addView(View child, ViewGroup.LayoutParams params) {
4611fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (getChildCount() > 0) {
4621fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            throw new IllegalStateException("ScrollView can host only one direct child");
4631fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
4641fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
4651fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        super.addView(child, params);
4661fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
4671fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
4681fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
4691fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public void addView(View child, int index, ViewGroup.LayoutParams params) {
4701fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (getChildCount() > 0) {
4711fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            throw new IllegalStateException("ScrollView can host only one direct child");
4721fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
4731fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
4741fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        super.addView(child, index, params);
4751fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
4761fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
4771fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
478cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes     * Register a callback to be invoked when the scroll X or Y positions of
479cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes     * this view change.
480cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes     * <p>This version of the method works on all versions of Android, back to API v4.</p>
481cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes     *
482cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes     * @param l The listener to notify when the scroll X or Y position changes.
483cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes     * @see android.view.View#getScrollX()
484cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes     * @see android.view.View#getScrollY()
485cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes     */
486852749684a10e7d104eed8fb2dcc68b64d079340Jake Wharton    public void setOnScrollChangeListener(@Nullable OnScrollChangeListener l) {
487cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes        mOnScrollChangeListener = l;
488cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes    }
489cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes
490cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes    /**
4911fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @return Returns true this ScrollView can be scrolled
4921fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
4931fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private boolean canScroll() {
49493ade4a86ccf8e87af43401a3b3e41a804e311a1shepshapard        if (getChildCount() > 0) {
49593ade4a86ccf8e87af43401a3b3e41a804e311a1shepshapard            View child = getChildAt(0);
49693ade4a86ccf8e87af43401a3b3e41a804e311a1shepshapard            final NestedScrollView.LayoutParams lp = (LayoutParams) child.getLayoutParams();
49793ade4a86ccf8e87af43401a3b3e41a804e311a1shepshapard            int childSize = child.getHeight() + lp.topMargin + lp.bottomMargin;
49893ade4a86ccf8e87af43401a3b3e41a804e311a1shepshapard            int parentSpace = getHeight() - getPaddingTop() - getPaddingBottom();
49993ade4a86ccf8e87af43401a3b3e41a804e311a1shepshapard            return childSize > parentSpace;
5001fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
5011fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return false;
5021fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
5031fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
5041fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
5051fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * Indicates whether this ScrollView's content is stretched to fill the viewport.
5061fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *
5071fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @return True if the content fills the viewport, false otherwise.
5081fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *
509929f27aab7ac7231f3734c988d5ee7201627d535Alan Viverette     * @attr name android:fillViewport
5101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
5111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public boolean isFillViewport() {
5121fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return mFillViewport;
5131fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
5141fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
5151fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
516236bf5bc5041e4bf356eac3855fd4fcf72cd6994Aurimas Liutikas     * Set whether this ScrollView should stretch its content height to fill the viewport or not.
5171fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *
5181fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @param fillViewport True to stretch the content's height to the viewport's
5191fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *        boundaries, false otherwise.
5201fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *
521929f27aab7ac7231f3734c988d5ee7201627d535Alan Viverette     * @attr name android:fillViewport
5221fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
5231fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public void setFillViewport(boolean fillViewport) {
5241fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (fillViewport != mFillViewport) {
5251fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            mFillViewport = fillViewport;
5261fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            requestLayout();
5271fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
5281fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
5291fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
5301fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
5311fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @return Whether arrow scrolling will animate its transition.
5321fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
5331fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public boolean isSmoothScrollingEnabled() {
5341fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return mSmoothScrollingEnabled;
5351fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
5361fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
5371fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
5381fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * Set whether arrow scrolling will animate its transition.
5391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @param smoothScrollingEnabled whether arrow scrolling will animate its transition
5401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
5411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public void setSmoothScrollingEnabled(boolean smoothScrollingEnabled) {
5421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        mSmoothScrollingEnabled = smoothScrollingEnabled;
5431fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
5441fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
5451fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
546cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
547cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes        super.onScrollChanged(l, t, oldl, oldt);
548cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes
549cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes        if (mOnScrollChangeListener != null) {
550cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes            mOnScrollChangeListener.onScrollChange(this, l, t, oldl, oldt);
551cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes        }
552cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes    }
553cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes
554cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes    @Override
5551fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
5561fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
5571fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
5581fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (!mFillViewport) {
5591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            return;
5601fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
5611fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
5621fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
5631fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (heightMode == MeasureSpec.UNSPECIFIED) {
5641fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            return;
5651fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
5661fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
5671fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (getChildCount() > 0) {
56893ade4a86ccf8e87af43401a3b3e41a804e311a1shepshapard            View child = getChildAt(0);
56993ade4a86ccf8e87af43401a3b3e41a804e311a1shepshapard            final NestedScrollView.LayoutParams lp = (LayoutParams) child.getLayoutParams();
57093ade4a86ccf8e87af43401a3b3e41a804e311a1shepshapard
57193ade4a86ccf8e87af43401a3b3e41a804e311a1shepshapard            int childSize = child.getMeasuredHeight();
57293ade4a86ccf8e87af43401a3b3e41a804e311a1shepshapard            int parentSpace = getMeasuredHeight()
57393ade4a86ccf8e87af43401a3b3e41a804e311a1shepshapard                    - getPaddingTop()
57493ade4a86ccf8e87af43401a3b3e41a804e311a1shepshapard                    - getPaddingBottom()
57593ade4a86ccf8e87af43401a3b3e41a804e311a1shepshapard                    - lp.topMargin
57693ade4a86ccf8e87af43401a3b3e41a804e311a1shepshapard                    - lp.bottomMargin;
5771fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
57893ade4a86ccf8e87af43401a3b3e41a804e311a1shepshapard            if (childSize < parentSpace) {
5791fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                int childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
58093ade4a86ccf8e87af43401a3b3e41a804e311a1shepshapard                        getPaddingLeft() + getPaddingRight() + lp.leftMargin + lp.rightMargin,
58193ade4a86ccf8e87af43401a3b3e41a804e311a1shepshapard                        lp.width);
5821fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                int childHeightMeasureSpec =
58393ade4a86ccf8e87af43401a3b3e41a804e311a1shepshapard                        MeasureSpec.makeMeasureSpec(parentSpace, MeasureSpec.EXACTLY);
5841fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
5851fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
5861fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
5871fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
5881fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
5891fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
5901fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public boolean dispatchKeyEvent(KeyEvent event) {
5911fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        // Let the focused view and/or our descendants get the key first
5921fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return super.dispatchKeyEvent(event) || executeKeyEvent(event);
5931fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
5941fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
5951fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
5961fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * You can call this function yourself to have the scroll view perform
5971fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * scrolling from a key event, just as if the event had been dispatched to
5981fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * it by the view hierarchy.
5991fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *
6001fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @param event The key event to execute.
6011fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @return Return true if the event was handled, else false.
6021fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
603852749684a10e7d104eed8fb2dcc68b64d079340Jake Wharton    public boolean executeKeyEvent(@NonNull KeyEvent event) {
6041fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        mTempRect.setEmpty();
6051fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
6061fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (!canScroll()) {
6071fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            if (isFocused() && event.getKeyCode() != KeyEvent.KEYCODE_BACK) {
6081fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                View currentFocused = findFocus();
6091fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                if (currentFocused == this) currentFocused = null;
6101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                View nextFocused = FocusFinder.getInstance().findNextFocus(this,
6111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        currentFocused, View.FOCUS_DOWN);
6121fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                return nextFocused != null
6131fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        && nextFocused != this
6141fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        && nextFocused.requestFocus(View.FOCUS_DOWN);
6151fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
6161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            return false;
6171fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
6181fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
6191fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        boolean handled = false;
6201fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (event.getAction() == KeyEvent.ACTION_DOWN) {
6211fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            switch (event.getKeyCode()) {
6221fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                case KeyEvent.KEYCODE_DPAD_UP:
6231fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    if (!event.isAltPressed()) {
6241fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        handled = arrowScroll(View.FOCUS_UP);
6251fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    } else {
6261fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        handled = fullScroll(View.FOCUS_UP);
6271fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    }
6281fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    break;
6291fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                case KeyEvent.KEYCODE_DPAD_DOWN:
6301fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    if (!event.isAltPressed()) {
6311fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        handled = arrowScroll(View.FOCUS_DOWN);
6321fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    } else {
6331fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        handled = fullScroll(View.FOCUS_DOWN);
6341fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    }
6351fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    break;
6361fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                case KeyEvent.KEYCODE_SPACE:
6371fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    pageScroll(event.isShiftPressed() ? View.FOCUS_UP : View.FOCUS_DOWN);
6381fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    break;
6391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
6401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
6411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
6421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return handled;
6431fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
6441fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
6451fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private boolean inChild(int x, int y) {
6461fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (getChildCount() > 0) {
6471fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            final int scrollY = getScrollY();
6481fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            final View child = getChildAt(0);
6491fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            return !(y < child.getTop() - scrollY
6501fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    || y >= child.getBottom() - scrollY
6511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    || x < child.getLeft()
6521fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    || x >= child.getRight());
6531fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
6541fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return false;
6551fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
6561fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
6571fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private void initOrResetVelocityTracker() {
6581fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (mVelocityTracker == null) {
6591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            mVelocityTracker = VelocityTracker.obtain();
6601fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        } else {
6611fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            mVelocityTracker.clear();
6621fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
6631fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
6641fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
6651fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private void initVelocityTrackerIfNotExists() {
6661fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (mVelocityTracker == null) {
6671fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            mVelocityTracker = VelocityTracker.obtain();
6681fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
6691fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
6701fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
6711fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private void recycleVelocityTracker() {
6721fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (mVelocityTracker != null) {
6731fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            mVelocityTracker.recycle();
6741fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            mVelocityTracker = null;
6751fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
6761fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
6771fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
6781fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
6791fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
6801fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (disallowIntercept) {
6811fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            recycleVelocityTracker();
6821fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
6831fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        super.requestDisallowInterceptTouchEvent(disallowIntercept);
6841fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
6851fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
6861fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
6871fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public boolean onInterceptTouchEvent(MotionEvent ev) {
6881fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        /*
6891fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell         * This method JUST determines whether we want to intercept the motion.
6901fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell         * If we return true, onMotionEvent will be called and we do the actual
6911fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell         * scrolling there.
6921fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell         */
6931fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
6941fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        /*
6951fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        * Shortcut the most recurring case: the user is in the dragging
6961fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        * state and he is moving his finger.  We want to intercept this
6971fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        * motion.
6981fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        */
6991fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        final int action = ev.getAction();
7001fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if ((action == MotionEvent.ACTION_MOVE) && (mIsBeingDragged)) {
7011fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            return true;
7021fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
7031fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
7046ed40c1f86bcb172a1f0f069cde1c571a7781aeeAurimas Liutikas        switch (action & MotionEvent.ACTION_MASK) {
7051fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            case MotionEvent.ACTION_MOVE: {
7061fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                /*
7071fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                 * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check
7081fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                 * whether the user has moved far enough from his original down touch.
7091fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                 */
7101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
7111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                /*
7121fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                * Locally do absolute value. mLastMotionY is set to the y value
7131fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                * of the down event.
7141fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                */
7151fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                final int activePointerId = mActivePointerId;
7161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                if (activePointerId == INVALID_POINTER) {
7171fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    // If we don't have a valid id, the touch down wasn't on content.
7181fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    break;
7191fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                }
7201fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
7211b3e9466b4c4d72f28bb4448672ef8bab19b6f3eKirill Grouchnikov                final int pointerIndex = ev.findPointerIndex(activePointerId);
7221fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                if (pointerIndex == -1) {
7231fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    Log.e(TAG, "Invalid pointerId=" + activePointerId
7241fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                            + " in onInterceptTouchEvent");
7251fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    break;
7261fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                }
7271fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
7281b3e9466b4c4d72f28bb4448672ef8bab19b6f3eKirill Grouchnikov                final int y = (int) ev.getY(pointerIndex);
7291fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                final int yDiff = Math.abs(y - mLastMotionY);
7301fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                if (yDiff > mTouchSlop
7311fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        && (getNestedScrollAxes() & ViewCompat.SCROLL_AXIS_VERTICAL) == 0) {
7321fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    mIsBeingDragged = true;
7331fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    mLastMotionY = y;
7341fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    initVelocityTrackerIfNotExists();
7351fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    mVelocityTracker.addMovement(ev);
7361fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    mNestedYOffset = 0;
7371fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    final ViewParent parent = getParent();
7381fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    if (parent != null) {
7391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        parent.requestDisallowInterceptTouchEvent(true);
7401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    }
7411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                }
7421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                break;
7431fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
7441fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
7451fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            case MotionEvent.ACTION_DOWN: {
7461fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                final int y = (int) ev.getY();
74776daed103193a1756535d1f59b165e98e1d17445Chris Banes                if (!inChild((int) ev.getX(), y)) {
7481fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    mIsBeingDragged = false;
7491fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    recycleVelocityTracker();
7501fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    break;
7511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                }
7521fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
7531fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                /*
7541fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                 * Remember location of down touch.
7551fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                 * ACTION_DOWN always refers to pointer index 0.
7561fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                 */
7571fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                mLastMotionY = y;
7581b3e9466b4c4d72f28bb4448672ef8bab19b6f3eKirill Grouchnikov                mActivePointerId = ev.getPointerId(0);
7591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
7601fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                initOrResetVelocityTracker();
7611fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                mVelocityTracker.addMovement(ev);
7621fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                /*
763661d25de05417a357540283ee0cd93b1326ac3a4Chris Banes                 * If being flinged and user touches the screen, initiate drag;
764661d25de05417a357540283ee0cd93b1326ac3a4Chris Banes                 * otherwise don't. mScroller.isFinished should be false when
765661d25de05417a357540283ee0cd93b1326ac3a4Chris Banes                 * being flinged. We need to call computeScrollOffset() first so that
766661d25de05417a357540283ee0cd93b1326ac3a4Chris Banes                 * isFinished() is correct.
7671fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                */
768661d25de05417a357540283ee0cd93b1326ac3a4Chris Banes                mScroller.computeScrollOffset();
7691fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                mIsBeingDragged = !mScroller.isFinished();
77076daed103193a1756535d1f59b165e98e1d17445Chris Banes                startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL, ViewCompat.TYPE_TOUCH);
7711fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                break;
7721fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
7731fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
7741fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            case MotionEvent.ACTION_CANCEL:
7751fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            case MotionEvent.ACTION_UP:
7761fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                /* Release the drag */
7771fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                mIsBeingDragged = false;
7781fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                mActivePointerId = INVALID_POINTER;
7791fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                recycleVelocityTracker();
78000db92e217c3bc08acd09143cac8e3d3b0d0e813Chris Banes                if (mScroller.springBack(getScrollX(), getScrollY(), 0, 0, 0, getScrollRange())) {
78100db92e217c3bc08acd09143cac8e3d3b0d0e813Chris Banes                    ViewCompat.postInvalidateOnAnimation(this);
78200db92e217c3bc08acd09143cac8e3d3b0d0e813Chris Banes                }
78376daed103193a1756535d1f59b165e98e1d17445Chris Banes                stopNestedScroll(ViewCompat.TYPE_TOUCH);
7841fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                break;
7856ed40c1f86bcb172a1f0f069cde1c571a7781aeeAurimas Liutikas            case MotionEvent.ACTION_POINTER_UP:
7861fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                onSecondaryPointerUp(ev);
7871fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                break;
7881fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
7891fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
7901fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        /*
7911fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        * The only time we want to intercept motion events is if we are in the
7921fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        * drag mode.
7931fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        */
7941fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return mIsBeingDragged;
7951fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
7961fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
7971fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
7981fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public boolean onTouchEvent(MotionEvent ev) {
7991fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        initVelocityTrackerIfNotExists();
8001fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
8011fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        MotionEvent vtev = MotionEvent.obtain(ev);
8021fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
8036ed40c1f86bcb172a1f0f069cde1c571a7781aeeAurimas Liutikas        final int actionMasked = ev.getActionMasked();
8041fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
8051fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (actionMasked == MotionEvent.ACTION_DOWN) {
8061fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            mNestedYOffset = 0;
8071fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
8081fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        vtev.offsetLocation(0, mNestedYOffset);
8091fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
8101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        switch (actionMasked) {
8111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            case MotionEvent.ACTION_DOWN: {
8121fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                if (getChildCount() == 0) {
8131fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    return false;
8141fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                }
8151fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                if ((mIsBeingDragged = !mScroller.isFinished())) {
8161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    final ViewParent parent = getParent();
8171fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    if (parent != null) {
8181fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        parent.requestDisallowInterceptTouchEvent(true);
8191fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    }
8201fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                }
8211fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
8221fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                /*
8231fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                 * If being flinged and user touches, stop the fling. isFinished
8241fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                 * will be false if being flinged.
8251fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                 */
8261fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                if (!mScroller.isFinished()) {
8271fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    mScroller.abortAnimation();
8281fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                }
8291fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
8301fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                // Remember where the motion event started
8311fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                mLastMotionY = (int) ev.getY();
8321b3e9466b4c4d72f28bb4448672ef8bab19b6f3eKirill Grouchnikov                mActivePointerId = ev.getPointerId(0);
83376daed103193a1756535d1f59b165e98e1d17445Chris Banes                startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL, ViewCompat.TYPE_TOUCH);
8341fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                break;
8351fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
8361fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            case MotionEvent.ACTION_MOVE:
8371b3e9466b4c4d72f28bb4448672ef8bab19b6f3eKirill Grouchnikov                final int activePointerIndex = ev.findPointerIndex(mActivePointerId);
8381fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                if (activePointerIndex == -1) {
8391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    Log.e(TAG, "Invalid pointerId=" + mActivePointerId + " in onTouchEvent");
8401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    break;
8411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                }
8421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
8431b3e9466b4c4d72f28bb4448672ef8bab19b6f3eKirill Grouchnikov                final int y = (int) ev.getY(activePointerIndex);
8441fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                int deltaY = mLastMotionY - y;
84576daed103193a1756535d1f59b165e98e1d17445Chris Banes                if (dispatchNestedPreScroll(0, deltaY, mScrollConsumed, mScrollOffset,
84676daed103193a1756535d1f59b165e98e1d17445Chris Banes                        ViewCompat.TYPE_TOUCH)) {
8471fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    deltaY -= mScrollConsumed[1];
8481fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    vtev.offsetLocation(0, mScrollOffset[1]);
8491fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    mNestedYOffset += mScrollOffset[1];
8501fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                }
8511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                if (!mIsBeingDragged && Math.abs(deltaY) > mTouchSlop) {
8521fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    final ViewParent parent = getParent();
8531fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    if (parent != null) {
8541fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        parent.requestDisallowInterceptTouchEvent(true);
8551fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    }
8561fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    mIsBeingDragged = true;
8571fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    if (deltaY > 0) {
8581fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        deltaY -= mTouchSlop;
8591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    } else {
8601fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        deltaY += mTouchSlop;
8611fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    }
8621fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                }
8631fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                if (mIsBeingDragged) {
8641fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    // Scroll to follow the motion event
8651fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    mLastMotionY = y - mScrollOffset[1];
8661fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
8671fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    final int oldY = getScrollY();
8681fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    final int range = getScrollRange();
869e12dfa03641ad9cf0ddf272675bbe7d1198adbfdAurimas Liutikas                    final int overscrollMode = getOverScrollMode();
870e12dfa03641ad9cf0ddf272675bbe7d1198adbfdAurimas Liutikas                    boolean canOverscroll = overscrollMode == View.OVER_SCROLL_ALWAYS
871e12dfa03641ad9cf0ddf272675bbe7d1198adbfdAurimas Liutikas                            || (overscrollMode == View.OVER_SCROLL_IF_CONTENT_SCROLLS && range > 0);
8721fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
8731fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    // Calling overScrollByCompat will call onOverScrolled, which
8741fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    // calls onScrollChanged if applicable.
8751fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    if (overScrollByCompat(0, deltaY, 0, getScrollY(), 0, range, 0,
87676daed103193a1756535d1f59b165e98e1d17445Chris Banes                            0, true) && !hasNestedScrollingParent(ViewCompat.TYPE_TOUCH)) {
8771fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        // Break our velocity if we hit a scroll barrier.
8781fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        mVelocityTracker.clear();
8791fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    }
8801fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
8811fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    final int scrolledDeltaY = getScrollY() - oldY;
8821fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    final int unconsumedY = deltaY - scrolledDeltaY;
88376daed103193a1756535d1f59b165e98e1d17445Chris Banes                    if (dispatchNestedScroll(0, scrolledDeltaY, 0, unconsumedY, mScrollOffset,
88476daed103193a1756535d1f59b165e98e1d17445Chris Banes                            ViewCompat.TYPE_TOUCH)) {
8851fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        mLastMotionY -= mScrollOffset[1];
8861fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        vtev.offsetLocation(0, mScrollOffset[1]);
8871fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        mNestedYOffset += mScrollOffset[1];
8881fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    } else if (canOverscroll) {
8891fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        ensureGlows();
8901fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        final int pulledToY = oldY + deltaY;
8911fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        if (pulledToY < 0) {
892506695b4cf6b22d70d2e3647d94551e721ee0430Aurimas Liutikas                            EdgeEffectCompat.onPull(mEdgeGlowTop, (float) deltaY / getHeight(),
8931b3e9466b4c4d72f28bb4448672ef8bab19b6f3eKirill Grouchnikov                                    ev.getX(activePointerIndex) / getWidth());
8941fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                            if (!mEdgeGlowBottom.isFinished()) {
8951fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                                mEdgeGlowBottom.onRelease();
8961fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                            }
8971fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        } else if (pulledToY > range) {
898506695b4cf6b22d70d2e3647d94551e721ee0430Aurimas Liutikas                            EdgeEffectCompat.onPull(mEdgeGlowBottom, (float) deltaY / getHeight(),
8991b3e9466b4c4d72f28bb4448672ef8bab19b6f3eKirill Grouchnikov                                    1.f - ev.getX(activePointerIndex)
9001fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                                            / getWidth());
9011fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                            if (!mEdgeGlowTop.isFinished()) {
9021fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                                mEdgeGlowTop.onRelease();
9031fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                            }
9041fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        }
9051fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        if (mEdgeGlowTop != null
9061fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                                && (!mEdgeGlowTop.isFinished() || !mEdgeGlowBottom.isFinished())) {
9071fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                            ViewCompat.postInvalidateOnAnimation(this);
9081fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        }
9091fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    }
9101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                }
9111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                break;
9121fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            case MotionEvent.ACTION_UP:
91376daed103193a1756535d1f59b165e98e1d17445Chris Banes                final VelocityTracker velocityTracker = mVelocityTracker;
91476daed103193a1756535d1f59b165e98e1d17445Chris Banes                velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
91576daed103193a1756535d1f59b165e98e1d17445Chris Banes                int initialVelocity = (int) velocityTracker.getYVelocity(mActivePointerId);
91676daed103193a1756535d1f59b165e98e1d17445Chris Banes                if ((Math.abs(initialVelocity) > mMinimumVelocity)) {
91776daed103193a1756535d1f59b165e98e1d17445Chris Banes                    flingWithNestedDispatch(-initialVelocity);
91876daed103193a1756535d1f59b165e98e1d17445Chris Banes                } else if (mScroller.springBack(getScrollX(), getScrollY(), 0, 0, 0,
91976daed103193a1756535d1f59b165e98e1d17445Chris Banes                        getScrollRange())) {
92076daed103193a1756535d1f59b165e98e1d17445Chris Banes                    ViewCompat.postInvalidateOnAnimation(this);
9211fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                }
922ba3bd1923ffd1ef5597deb0a9df8c1444fbf4433Chris Banes                mActivePointerId = INVALID_POINTER;
923ba3bd1923ffd1ef5597deb0a9df8c1444fbf4433Chris Banes                endDrag();
9241fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                break;
9251fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            case MotionEvent.ACTION_CANCEL:
9261fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                if (mIsBeingDragged && getChildCount() > 0) {
92700db92e217c3bc08acd09143cac8e3d3b0d0e813Chris Banes                    if (mScroller.springBack(getScrollX(), getScrollY(), 0, 0, 0,
92800db92e217c3bc08acd09143cac8e3d3b0d0e813Chris Banes                            getScrollRange())) {
92900db92e217c3bc08acd09143cac8e3d3b0d0e813Chris Banes                        ViewCompat.postInvalidateOnAnimation(this);
93000db92e217c3bc08acd09143cac8e3d3b0d0e813Chris Banes                    }
9311fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                }
932ba3bd1923ffd1ef5597deb0a9df8c1444fbf4433Chris Banes                mActivePointerId = INVALID_POINTER;
933ba3bd1923ffd1ef5597deb0a9df8c1444fbf4433Chris Banes                endDrag();
9341fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                break;
9356ed40c1f86bcb172a1f0f069cde1c571a7781aeeAurimas Liutikas            case MotionEvent.ACTION_POINTER_DOWN: {
9366ed40c1f86bcb172a1f0f069cde1c571a7781aeeAurimas Liutikas                final int index = ev.getActionIndex();
9371b3e9466b4c4d72f28bb4448672ef8bab19b6f3eKirill Grouchnikov                mLastMotionY = (int) ev.getY(index);
9381b3e9466b4c4d72f28bb4448672ef8bab19b6f3eKirill Grouchnikov                mActivePointerId = ev.getPointerId(index);
9391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                break;
9401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
9416ed40c1f86bcb172a1f0f069cde1c571a7781aeeAurimas Liutikas            case MotionEvent.ACTION_POINTER_UP:
9421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                onSecondaryPointerUp(ev);
9431b3e9466b4c4d72f28bb4448672ef8bab19b6f3eKirill Grouchnikov                mLastMotionY = (int) ev.getY(ev.findPointerIndex(mActivePointerId));
9441fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                break;
9451fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
9461fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
9471fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (mVelocityTracker != null) {
9481fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            mVelocityTracker.addMovement(vtev);
9491fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
9501fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        vtev.recycle();
9511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return true;
9521fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
9531fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
9541fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private void onSecondaryPointerUp(MotionEvent ev) {
9556ed40c1f86bcb172a1f0f069cde1c571a7781aeeAurimas Liutikas        final int pointerIndex = ev.getActionIndex();
9561b3e9466b4c4d72f28bb4448672ef8bab19b6f3eKirill Grouchnikov        final int pointerId = ev.getPointerId(pointerIndex);
9571fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (pointerId == mActivePointerId) {
9581fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            // This was our active pointer going up. Choose a new
9591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            // active pointer and adjust accordingly.
9601fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            // TODO: Make this decision more intelligent.
9611fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
9621b3e9466b4c4d72f28bb4448672ef8bab19b6f3eKirill Grouchnikov            mLastMotionY = (int) ev.getY(newPointerIndex);
9631b3e9466b4c4d72f28bb4448672ef8bab19b6f3eKirill Grouchnikov            mActivePointerId = ev.getPointerId(newPointerIndex);
9641fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            if (mVelocityTracker != null) {
9651fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                mVelocityTracker.clear();
9661fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
9671fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
9681fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
9691fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
970e2104f4b5c8e3ad63570306a25e61502dfe4c418Aurimas Liutikas    @Override
9711fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public boolean onGenericMotionEvent(MotionEvent event) {
9721b3e9466b4c4d72f28bb4448672ef8bab19b6f3eKirill Grouchnikov        if ((event.getSource() & InputDeviceCompat.SOURCE_CLASS_POINTER) != 0) {
9731fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            switch (event.getAction()) {
9746ed40c1f86bcb172a1f0f069cde1c571a7781aeeAurimas Liutikas                case MotionEvent.ACTION_SCROLL: {
9751fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    if (!mIsBeingDragged) {
9766ed40c1f86bcb172a1f0f069cde1c571a7781aeeAurimas Liutikas                        final float vscroll = event.getAxisValue(MotionEvent.AXIS_VSCROLL);
9771fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        if (vscroll != 0) {
9781fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                            final int delta = (int) (vscroll * getVerticalScrollFactorCompat());
9791fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                            final int range = getScrollRange();
9801fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                            int oldScrollY = getScrollY();
9811fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                            int newScrollY = oldScrollY - delta;
9821fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                            if (newScrollY < 0) {
9831fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                                newScrollY = 0;
9841fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                            } else if (newScrollY > range) {
9851fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                                newScrollY = range;
9861fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                            }
9871fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                            if (newScrollY != oldScrollY) {
9881fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                                super.scrollTo(getScrollX(), newScrollY);
9891fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                                return true;
9901fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                            }
9911fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        }
9921fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    }
9931fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                }
9941fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
9951fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
9961fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return false;
9971fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
9981fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
9991fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private float getVerticalScrollFactorCompat() {
10001fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (mVerticalScrollFactor == 0) {
10011fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            TypedValue outValue = new TypedValue();
10021fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            final Context context = getContext();
10031fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            if (!context.getTheme().resolveAttribute(
10041fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    android.R.attr.listPreferredItemHeight, outValue, true)) {
10051fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                throw new IllegalStateException(
10061fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        "Expected theme to define listPreferredItemHeight.");
10071fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
10081fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            mVerticalScrollFactor = outValue.getDimension(
10091fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    context.getResources().getDisplayMetrics());
10101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
10111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return mVerticalScrollFactor;
10121fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
10131fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
101415375aa6fd54b036f97f99229aefab2822c8a1c9Aurimas Liutikas    @Override
10151fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    protected void onOverScrolled(int scrollX, int scrollY,
10161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            boolean clampedX, boolean clampedY) {
10171fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        super.scrollTo(scrollX, scrollY);
10181fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
10191fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
10201fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    boolean overScrollByCompat(int deltaX, int deltaY,
10211fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            int scrollX, int scrollY,
10221fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            int scrollRangeX, int scrollRangeY,
10231fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            int maxOverScrollX, int maxOverScrollY,
10241fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            boolean isTouchEvent) {
1025e12dfa03641ad9cf0ddf272675bbe7d1198adbfdAurimas Liutikas        final int overScrollMode = getOverScrollMode();
10261fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        final boolean canScrollHorizontal =
10271fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                computeHorizontalScrollRange() > computeHorizontalScrollExtent();
10281fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        final boolean canScrollVertical =
10291fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                computeVerticalScrollRange() > computeVerticalScrollExtent();
1030e12dfa03641ad9cf0ddf272675bbe7d1198adbfdAurimas Liutikas        final boolean overScrollHorizontal = overScrollMode == View.OVER_SCROLL_ALWAYS
1031e12dfa03641ad9cf0ddf272675bbe7d1198adbfdAurimas Liutikas                || (overScrollMode == View.OVER_SCROLL_IF_CONTENT_SCROLLS && canScrollHorizontal);
1032e12dfa03641ad9cf0ddf272675bbe7d1198adbfdAurimas Liutikas        final boolean overScrollVertical = overScrollMode == View.OVER_SCROLL_ALWAYS
1033e12dfa03641ad9cf0ddf272675bbe7d1198adbfdAurimas Liutikas                || (overScrollMode == View.OVER_SCROLL_IF_CONTENT_SCROLLS && canScrollVertical);
10341fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
10351fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        int newScrollX = scrollX + deltaX;
10361fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (!overScrollHorizontal) {
10371fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            maxOverScrollX = 0;
10381fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
10391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
10401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        int newScrollY = scrollY + deltaY;
10411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (!overScrollVertical) {
10421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            maxOverScrollY = 0;
10431fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
10441fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
10451fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        // Clamp values if at the limits and record
10461fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        final int left = -maxOverScrollX;
10471fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        final int right = maxOverScrollX + scrollRangeX;
10481fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        final int top = -maxOverScrollY;
10491fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        final int bottom = maxOverScrollY + scrollRangeY;
10501fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
10511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        boolean clampedX = false;
10521fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (newScrollX > right) {
10531fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            newScrollX = right;
10541fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            clampedX = true;
10551fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        } else if (newScrollX < left) {
10561fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            newScrollX = left;
10571fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            clampedX = true;
10581fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
10591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
10601fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        boolean clampedY = false;
10611fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (newScrollY > bottom) {
10621fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            newScrollY = bottom;
10631fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            clampedY = true;
10641fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        } else if (newScrollY < top) {
10651fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            newScrollY = top;
10661fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            clampedY = true;
10671fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
10681fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
106976daed103193a1756535d1f59b165e98e1d17445Chris Banes        if (clampedY && !hasNestedScrollingParent(ViewCompat.TYPE_NON_TOUCH)) {
107000db92e217c3bc08acd09143cac8e3d3b0d0e813Chris Banes            mScroller.springBack(newScrollX, newScrollY, 0, 0, 0, getScrollRange());
107100db92e217c3bc08acd09143cac8e3d3b0d0e813Chris Banes        }
107200db92e217c3bc08acd09143cac8e3d3b0d0e813Chris Banes
10731fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        onOverScrolled(newScrollX, newScrollY, clampedX, clampedY);
10741fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
10751fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return clampedX || clampedY;
10761fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
10771fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
1078540222c3675801eaa141ace1c164c4d3499b4721Aurimas Liutikas    int getScrollRange() {
10791fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        int scrollRange = 0;
10801fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (getChildCount() > 0) {
10811fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            View child = getChildAt(0);
108293ade4a86ccf8e87af43401a3b3e41a804e311a1shepshapard            NestedScrollView.LayoutParams lp = (LayoutParams) child.getLayoutParams();
108393ade4a86ccf8e87af43401a3b3e41a804e311a1shepshapard            int childSize = child.getHeight() + lp.topMargin + lp.bottomMargin;
108493ade4a86ccf8e87af43401a3b3e41a804e311a1shepshapard            int parentSpace = getHeight() - getPaddingTop() - getPaddingBottom();
108593ade4a86ccf8e87af43401a3b3e41a804e311a1shepshapard            scrollRange = Math.max(0, childSize - parentSpace);
10861fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
10871fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return scrollRange;
10881fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
10891fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
10901fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
10911fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * <p>
10921fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * Finds the next focusable component that fits in the specified bounds.
10931fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * </p>
10941fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *
10951fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @param topFocus look for a candidate is the one at the top of the bounds
10961fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *                 if topFocus is true, or at the bottom of the bounds if topFocus is
10971fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *                 false
10981fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @param top      the top offset of the bounds in which a focusable must be
10991fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *                 found
11001fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @param bottom   the bottom offset of the bounds in which a focusable must
11011fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *                 be found
11021fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @return the next focusable component in the bounds or null if none can
11031fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *         be found
11041fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
11051fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private View findFocusableViewInBounds(boolean topFocus, int top, int bottom) {
11061fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
11071fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        List<View> focusables = getFocusables(View.FOCUS_FORWARD);
11081fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        View focusCandidate = null;
11091fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
11101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        /*
11111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell         * A fully contained focusable is one where its top is below the bound's
11121fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell         * top, and its bottom is above the bound's bottom. A partially
11131fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell         * contained focusable is one where some part of it is within the
11141fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell         * bounds, but it also has some part that is not within bounds.  A fully contained
11151fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell         * focusable is preferred to a partially contained focusable.
11161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell         */
11171fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        boolean foundFullyContainedFocusable = false;
11181fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
11191fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        int count = focusables.size();
11201fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        for (int i = 0; i < count; i++) {
11211fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            View view = focusables.get(i);
11221fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            int viewTop = view.getTop();
11231fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            int viewBottom = view.getBottom();
11241fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
11251fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            if (top < viewBottom && viewTop < bottom) {
11261fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                /*
11271fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                 * the focusable is in the target area, it is a candidate for
11281fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                 * focusing
11291fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                 */
11301fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
11314d2c7b7c4f194034c5f17c4bee7320d808aabe4cAurimas Liutikas                final boolean viewIsFullyContained = (top < viewTop) && (viewBottom < bottom);
11321fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
11331fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                if (focusCandidate == null) {
11341fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    /* No candidate, take this one */
11351fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    focusCandidate = view;
11361fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    foundFullyContainedFocusable = viewIsFullyContained;
11371fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                } else {
11381fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    final boolean viewIsCloserToBoundary =
11394d2c7b7c4f194034c5f17c4bee7320d808aabe4cAurimas Liutikas                            (topFocus && viewTop < focusCandidate.getTop())
11404d2c7b7c4f194034c5f17c4bee7320d808aabe4cAurimas Liutikas                                    || (!topFocus && viewBottom > focusCandidate.getBottom());
11411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
11421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    if (foundFullyContainedFocusable) {
11431fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        if (viewIsFullyContained && viewIsCloserToBoundary) {
11441fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                            /*
11451fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                             * We're dealing with only fully contained views, so
11461fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                             * it has to be closer to the boundary to beat our
11471fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                             * candidate
11481fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                             */
11491fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                            focusCandidate = view;
11501fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        }
11511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    } else {
11521fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        if (viewIsFullyContained) {
11531fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                            /* Any fully contained view beats a partially contained view */
11541fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                            focusCandidate = view;
11551fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                            foundFullyContainedFocusable = true;
11561fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        } else if (viewIsCloserToBoundary) {
11571fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                            /*
11581fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                             * Partially contained view beats another partially
11591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                             * contained view if it's closer
11601fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                             */
11611fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                            focusCandidate = view;
11621fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        }
11631fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    }
11641fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                }
11651fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
11661fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
11671fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
11681fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return focusCandidate;
11691fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
11701fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
11711fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
11721fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * <p>Handles scrolling in response to a "page up/down" shortcut press. This
11731fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * method will scroll the view by one page up or down and give the focus
11741fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * to the topmost/bottommost component in the new visible area. If no
11751fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * component is a good candidate for focus, this scrollview reclaims the
11761fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * focus.</p>
11771fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *
11781fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @param direction the scroll direction: {@link android.view.View#FOCUS_UP}
11791fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *                  to go one page up or
11801fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *                  {@link android.view.View#FOCUS_DOWN} to go one page down
11811fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @return true if the key event is consumed by this method, false otherwise
11821fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
11831fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public boolean pageScroll(int direction) {
11841fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        boolean down = direction == View.FOCUS_DOWN;
11851fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        int height = getHeight();
11861fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
11871fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (down) {
11881fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            mTempRect.top = getScrollY() + height;
11891fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            int count = getChildCount();
11901fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            if (count > 0) {
11911fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                View view = getChildAt(count - 1);
119293ade4a86ccf8e87af43401a3b3e41a804e311a1shepshapard                NestedScrollView.LayoutParams lp = (LayoutParams) view.getLayoutParams();
119393ade4a86ccf8e87af43401a3b3e41a804e311a1shepshapard                int bottom = view.getBottom() + lp.bottomMargin + getPaddingBottom();
119493ade4a86ccf8e87af43401a3b3e41a804e311a1shepshapard                if (mTempRect.top + height > bottom) {
119593ade4a86ccf8e87af43401a3b3e41a804e311a1shepshapard                    mTempRect.top = bottom - height;
11961fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                }
11971fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
11981fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        } else {
11991fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            mTempRect.top = getScrollY() - height;
12001fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            if (mTempRect.top < 0) {
12011fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                mTempRect.top = 0;
12021fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
12031fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
12041fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        mTempRect.bottom = mTempRect.top + height;
12051fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
12061fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return scrollAndFocus(direction, mTempRect.top, mTempRect.bottom);
12071fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
12081fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
12091fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
12101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * <p>Handles scrolling in response to a "home/end" shortcut press. This
12111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * method will scroll the view to the top or bottom and give the focus
12121fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * to the topmost/bottommost component in the new visible area. If no
12131fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * component is a good candidate for focus, this scrollview reclaims the
12141fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * focus.</p>
12151fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *
12161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @param direction the scroll direction: {@link android.view.View#FOCUS_UP}
12171fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *                  to go the top of the view or
12181fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *                  {@link android.view.View#FOCUS_DOWN} to go the bottom
12191fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @return true if the key event is consumed by this method, false otherwise
12201fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
12211fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public boolean fullScroll(int direction) {
12221fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        boolean down = direction == View.FOCUS_DOWN;
12231fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        int height = getHeight();
12241fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
12251fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        mTempRect.top = 0;
12261fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        mTempRect.bottom = height;
12271fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
12281fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (down) {
12291fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            int count = getChildCount();
12301fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            if (count > 0) {
12311fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                View view = getChildAt(count - 1);
123293ade4a86ccf8e87af43401a3b3e41a804e311a1shepshapard                NestedScrollView.LayoutParams lp = (LayoutParams) view.getLayoutParams();
123393ade4a86ccf8e87af43401a3b3e41a804e311a1shepshapard                mTempRect.bottom = view.getBottom() + lp.bottomMargin + getPaddingBottom();
12341fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                mTempRect.top = mTempRect.bottom - height;
12351fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
12361fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
12371fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
12381fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return scrollAndFocus(direction, mTempRect.top, mTempRect.bottom);
12391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
12401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
12411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
12421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * <p>Scrolls the view to make the area defined by <code>top</code> and
12431fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * <code>bottom</code> visible. This method attempts to give the focus
12441fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * to a component visible in this area. If no component can be focused in
12451fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * the new visible area, the focus is reclaimed by this ScrollView.</p>
12461fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *
12471fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @param direction the scroll direction: {@link android.view.View#FOCUS_UP}
12481fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *                  to go upward, {@link android.view.View#FOCUS_DOWN} to downward
12491fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @param top       the top offset of the new area to be made visible
12501fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @param bottom    the bottom offset of the new area to be made visible
12511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @return true if the key event is consumed by this method, false otherwise
12521fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
12531fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private boolean scrollAndFocus(int direction, int top, int bottom) {
12541fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        boolean handled = true;
12551fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
12561fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        int height = getHeight();
12571fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        int containerTop = getScrollY();
12581fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        int containerBottom = containerTop + height;
12591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        boolean up = direction == View.FOCUS_UP;
12601fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
12611fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        View newFocused = findFocusableViewInBounds(up, top, bottom);
12621fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (newFocused == null) {
12631fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            newFocused = this;
12641fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
12651fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
12661fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (top >= containerTop && bottom <= containerBottom) {
12671fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            handled = false;
12681fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        } else {
12691fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            int delta = up ? (top - containerTop) : (bottom - containerBottom);
12701fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            doScrollY(delta);
12711fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
12721fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
12731fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (newFocused != findFocus()) newFocused.requestFocus(direction);
12741fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
12751fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return handled;
12761fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
12771fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
12781fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
12791fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * Handle scrolling in response to an up or down arrow click.
12801fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *
12811fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @param direction The direction corresponding to the arrow key that was
12821fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *                  pressed
12831fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @return True if we consumed the event, false otherwise
12841fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
12851fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public boolean arrowScroll(int direction) {
12861fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        View currentFocused = findFocus();
12871fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (currentFocused == this) currentFocused = null;
12881fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
12891fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        View nextFocused = FocusFinder.getInstance().findNextFocus(this, currentFocused, direction);
12901fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
12911fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        final int maxJump = getMaxScrollAmount();
12921fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
12931fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (nextFocused != null && isWithinDeltaOfScreen(nextFocused, maxJump, getHeight())) {
12941fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            nextFocused.getDrawingRect(mTempRect);
12951fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            offsetDescendantRectToMyCoords(nextFocused, mTempRect);
12961fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            int scrollDelta = computeScrollDeltaToGetChildRectOnScreen(mTempRect);
12971fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            doScrollY(scrollDelta);
12981fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            nextFocused.requestFocus(direction);
12991fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        } else {
13001fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            // no new focus
13011fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            int scrollDelta = maxJump;
13021fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
13031fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            if (direction == View.FOCUS_UP && getScrollY() < scrollDelta) {
13041fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                scrollDelta = getScrollY();
13051fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            } else if (direction == View.FOCUS_DOWN) {
13061fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                if (getChildCount() > 0) {
130793ade4a86ccf8e87af43401a3b3e41a804e311a1shepshapard                    View child = getChildAt(0);
130893ade4a86ccf8e87af43401a3b3e41a804e311a1shepshapard                    NestedScrollView.LayoutParams lp = (LayoutParams) child.getLayoutParams();
130993ade4a86ccf8e87af43401a3b3e41a804e311a1shepshapard                    int daBottom = child.getBottom() + lp.bottomMargin;
13101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    int screenBottom = getScrollY() + getHeight() - getPaddingBottom();
131193ade4a86ccf8e87af43401a3b3e41a804e311a1shepshapard                    scrollDelta = Math.min(daBottom - screenBottom, maxJump);
13121fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                }
13131fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
13141fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            if (scrollDelta == 0) {
13151fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                return false;
13161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
13171fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            doScrollY(direction == View.FOCUS_DOWN ? scrollDelta : -scrollDelta);
13181fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
13191fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
13201fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (currentFocused != null && currentFocused.isFocused()
13211fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                && isOffScreen(currentFocused)) {
13221fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            // previously focused item still has focus and is off screen, give
13231fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            // it up (take it back to ourselves)
13241fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            // (also, need to temporarily force FOCUS_BEFORE_DESCENDANTS so we are
13251fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            // sure to
13261fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            // get it)
13271fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            final int descendantFocusability = getDescendantFocusability();  // save
13281fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
13291fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            requestFocus();
13301fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            setDescendantFocusability(descendantFocusability);  // restore
13311fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
13321fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return true;
13331fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
13341fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
13351fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
13361fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @return whether the descendant of this scroll view is scrolled off
13371fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *  screen.
13381fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
13391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private boolean isOffScreen(View descendant) {
13401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return !isWithinDeltaOfScreen(descendant, 0, getHeight());
13411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
13421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
13431fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
13441fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @return whether the descendant of this scroll view is within delta
13451fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *  pixels of being on the screen.
13461fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
13471fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private boolean isWithinDeltaOfScreen(View descendant, int delta, int height) {
13481fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        descendant.getDrawingRect(mTempRect);
13491fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        offsetDescendantRectToMyCoords(descendant, mTempRect);
13501fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
13511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return (mTempRect.bottom + delta) >= getScrollY()
13521fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                && (mTempRect.top - delta) <= (getScrollY() + height);
13531fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
13541fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
13551fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
13561fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * Smooth scroll by a Y delta
13571fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *
13581fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @param delta the number of pixels to scroll by on the Y axis
13591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
13601fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private void doScrollY(int delta) {
13611fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (delta != 0) {
13621fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            if (mSmoothScrollingEnabled) {
13631fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                smoothScrollBy(0, delta);
13641fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            } else {
13651fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                scrollBy(0, delta);
13661fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
13671fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
13681fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
13691fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
13701fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
13711fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * Like {@link View#scrollBy}, but scroll smoothly instead of immediately.
13721fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *
13731fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @param dx the number of pixels to scroll by on the X axis
13741fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @param dy the number of pixels to scroll by on the Y axis
13751fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
13761fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public final void smoothScrollBy(int dx, int dy) {
13771fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (getChildCount() == 0) {
13781fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            // Nothing to do.
13791fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            return;
13801fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
13811fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        long duration = AnimationUtils.currentAnimationTimeMillis() - mLastScroll;
13821fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (duration > ANIMATED_SCROLL_GAP) {
138393ade4a86ccf8e87af43401a3b3e41a804e311a1shepshapard            View child = getChildAt(0);
138493ade4a86ccf8e87af43401a3b3e41a804e311a1shepshapard            NestedScrollView.LayoutParams lp = (LayoutParams) child.getLayoutParams();
138593ade4a86ccf8e87af43401a3b3e41a804e311a1shepshapard            int childSize = child.getHeight() + lp.topMargin + lp.bottomMargin;
138693ade4a86ccf8e87af43401a3b3e41a804e311a1shepshapard            int parentSpace = getHeight() - getPaddingTop() - getPaddingBottom();
13871fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            final int scrollY = getScrollY();
138893ade4a86ccf8e87af43401a3b3e41a804e311a1shepshapard            final int maxY = Math.max(0, childSize - parentSpace);
13891fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            dy = Math.max(0, Math.min(scrollY + dy, maxY)) - scrollY;
139093ade4a86ccf8e87af43401a3b3e41a804e311a1shepshapard            mLastScrollerY = getScrollY();
13911fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            mScroller.startScroll(getScrollX(), scrollY, 0, dy);
13921fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            ViewCompat.postInvalidateOnAnimation(this);
13931fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        } else {
13941fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            if (!mScroller.isFinished()) {
13951fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                mScroller.abortAnimation();
13961fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
13971fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            scrollBy(dx, dy);
13981fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
13991fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        mLastScroll = AnimationUtils.currentAnimationTimeMillis();
14001fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
14011fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
14021fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
14031fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * Like {@link #scrollTo}, but scroll smoothly instead of immediately.
14041fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *
14051fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @param x the position where to scroll on the X axis
14061fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @param y the position where to scroll on the Y axis
14071fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
14081fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public final void smoothScrollTo(int x, int y) {
14091fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        smoothScrollBy(x - getScrollX(), y - getScrollY());
14101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
14111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
14121fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
14131fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * <p>The scroll range of a scroll view is the overall height of all of its
14141fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * children.</p>
1415c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki     * @hide
14161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
14178e10080c914d1ad0784394fa3026b85535535847Aurimas Liutikas    @RestrictTo(LIBRARY_GROUP)
14181fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
1419c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki    public int computeVerticalScrollRange() {
14201fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        final int count = getChildCount();
142193ade4a86ccf8e87af43401a3b3e41a804e311a1shepshapard        final int parentSpace = getHeight() - getPaddingBottom() - getPaddingTop();
14221fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (count == 0) {
142393ade4a86ccf8e87af43401a3b3e41a804e311a1shepshapard            return parentSpace;
14241fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
14251fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
142693ade4a86ccf8e87af43401a3b3e41a804e311a1shepshapard        View child = getChildAt(0);
142793ade4a86ccf8e87af43401a3b3e41a804e311a1shepshapard        NestedScrollView.LayoutParams lp = (LayoutParams) child.getLayoutParams();
142893ade4a86ccf8e87af43401a3b3e41a804e311a1shepshapard        int scrollRange = child.getBottom() + lp.bottomMargin;
14291fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        final int scrollY = getScrollY();
143093ade4a86ccf8e87af43401a3b3e41a804e311a1shepshapard        final int overscrollBottom = Math.max(0, scrollRange - parentSpace);
14311fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (scrollY < 0) {
14321fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            scrollRange -= scrollY;
14331fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        } else if (scrollY > overscrollBottom) {
14341fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            scrollRange += scrollY - overscrollBottom;
14351fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
14361fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
14371fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return scrollRange;
14381fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
14391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
1440c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki    /** @hide */
14418e10080c914d1ad0784394fa3026b85535535847Aurimas Liutikas    @RestrictTo(LIBRARY_GROUP)
14421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
1443c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki    public int computeVerticalScrollOffset() {
14441fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return Math.max(0, super.computeVerticalScrollOffset());
14451fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
14461fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
1447c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki    /** @hide */
14488e10080c914d1ad0784394fa3026b85535535847Aurimas Liutikas    @RestrictTo(LIBRARY_GROUP)
1449c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki    @Override
1450c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki    public int computeVerticalScrollExtent() {
1451c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki        return super.computeVerticalScrollExtent();
1452c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki    }
1453c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki
1454c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki    /** @hide */
14558e10080c914d1ad0784394fa3026b85535535847Aurimas Liutikas    @RestrictTo(LIBRARY_GROUP)
1456c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki    @Override
1457c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki    public int computeHorizontalScrollRange() {
1458c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki        return super.computeHorizontalScrollRange();
1459c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki    }
1460c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki
1461c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki    /** @hide */
14628e10080c914d1ad0784394fa3026b85535535847Aurimas Liutikas    @RestrictTo(LIBRARY_GROUP)
1463c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki    @Override
1464c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki    public int computeHorizontalScrollOffset() {
1465c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki        return super.computeHorizontalScrollOffset();
1466c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki    }
1467c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki
1468c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki    /** @hide */
14698e10080c914d1ad0784394fa3026b85535535847Aurimas Liutikas    @RestrictTo(LIBRARY_GROUP)
1470c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki    @Override
1471c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki    public int computeHorizontalScrollExtent() {
1472c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki        return super.computeHorizontalScrollExtent();
1473c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki    }
1474c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki
14751fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
14764d2c7b7c4f194034c5f17c4bee7320d808aabe4cAurimas Liutikas    protected void measureChild(View child, int parentWidthMeasureSpec,
14774d2c7b7c4f194034c5f17c4bee7320d808aabe4cAurimas Liutikas            int parentHeightMeasureSpec) {
14781fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        ViewGroup.LayoutParams lp = child.getLayoutParams();
14791fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
14801fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        int childWidthMeasureSpec;
14811fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        int childHeightMeasureSpec;
14821fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
14831fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, getPaddingLeft()
14841fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                + getPaddingRight(), lp.width);
14851fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
14861fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
14871fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
14881fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
14891fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
14901fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
14911fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
14921fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed,
14931fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            int parentHeightMeasureSpec, int heightUsed) {
14941fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
14951fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
14961fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
14971fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                getPaddingLeft() + getPaddingRight() + lp.leftMargin + lp.rightMargin
14981fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        + widthUsed, lp.width);
14991fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        final int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
15001fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                lp.topMargin + lp.bottomMargin, MeasureSpec.UNSPECIFIED);
15011fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
15021fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
15031fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
15041fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
15051fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
15061fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public void computeScroll() {
15071fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (mScroller.computeScrollOffset()) {
150876daed103193a1756535d1f59b165e98e1d17445Chris Banes            final int x = mScroller.getCurrX();
150976daed103193a1756535d1f59b165e98e1d17445Chris Banes            final int y = mScroller.getCurrY();
151076daed103193a1756535d1f59b165e98e1d17445Chris Banes
151176daed103193a1756535d1f59b165e98e1d17445Chris Banes            int dy = y - mLastScrollerY;
15121fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
151376daed103193a1756535d1f59b165e98e1d17445Chris Banes            // Dispatch up to parent
151476daed103193a1756535d1f59b165e98e1d17445Chris Banes            if (dispatchNestedPreScroll(0, dy, mScrollConsumed, null, ViewCompat.TYPE_NON_TOUCH)) {
151576daed103193a1756535d1f59b165e98e1d17445Chris Banes                dy -= mScrollConsumed[1];
151676daed103193a1756535d1f59b165e98e1d17445Chris Banes            }
151776daed103193a1756535d1f59b165e98e1d17445Chris Banes
151876daed103193a1756535d1f59b165e98e1d17445Chris Banes            if (dy != 0) {
15191fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                final int range = getScrollRange();
152076daed103193a1756535d1f59b165e98e1d17445Chris Banes                final int oldScrollY = getScrollY();
152176daed103193a1756535d1f59b165e98e1d17445Chris Banes
152276daed103193a1756535d1f59b165e98e1d17445Chris Banes                overScrollByCompat(0, dy, getScrollX(), oldScrollY, 0, range, 0, 0, false);
152376daed103193a1756535d1f59b165e98e1d17445Chris Banes
152476daed103193a1756535d1f59b165e98e1d17445Chris Banes                final int scrolledDeltaY = getScrollY() - oldScrollY;
152576daed103193a1756535d1f59b165e98e1d17445Chris Banes                final int unconsumedY = dy - scrolledDeltaY;
152676daed103193a1756535d1f59b165e98e1d17445Chris Banes
152776daed103193a1756535d1f59b165e98e1d17445Chris Banes                if (!dispatchNestedScroll(0, scrolledDeltaY, 0, unconsumedY, null,
152876daed103193a1756535d1f59b165e98e1d17445Chris Banes                        ViewCompat.TYPE_NON_TOUCH)) {
152976daed103193a1756535d1f59b165e98e1d17445Chris Banes                    final int mode = getOverScrollMode();
153076daed103193a1756535d1f59b165e98e1d17445Chris Banes                    final boolean canOverscroll = mode == OVER_SCROLL_ALWAYS
153176daed103193a1756535d1f59b165e98e1d17445Chris Banes                            || (mode == OVER_SCROLL_IF_CONTENT_SCROLLS && range > 0);
153276daed103193a1756535d1f59b165e98e1d17445Chris Banes                    if (canOverscroll) {
153376daed103193a1756535d1f59b165e98e1d17445Chris Banes                        ensureGlows();
153476daed103193a1756535d1f59b165e98e1d17445Chris Banes                        if (y <= 0 && oldScrollY > 0) {
153576daed103193a1756535d1f59b165e98e1d17445Chris Banes                            mEdgeGlowTop.onAbsorb((int) mScroller.getCurrVelocity());
153676daed103193a1756535d1f59b165e98e1d17445Chris Banes                        } else if (y >= range && oldScrollY < range) {
153776daed103193a1756535d1f59b165e98e1d17445Chris Banes                            mEdgeGlowBottom.onAbsorb((int) mScroller.getCurrVelocity());
153876daed103193a1756535d1f59b165e98e1d17445Chris Banes                        }
15391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    }
15401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                }
15411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
154276daed103193a1756535d1f59b165e98e1d17445Chris Banes
154376daed103193a1756535d1f59b165e98e1d17445Chris Banes            // Finally update the scroll positions and post an invalidation
154476daed103193a1756535d1f59b165e98e1d17445Chris Banes            mLastScrollerY = y;
154576daed103193a1756535d1f59b165e98e1d17445Chris Banes            ViewCompat.postInvalidateOnAnimation(this);
154676daed103193a1756535d1f59b165e98e1d17445Chris Banes        } else {
154776daed103193a1756535d1f59b165e98e1d17445Chris Banes            // We can't scroll any more, so stop any indirect scrolling
154876daed103193a1756535d1f59b165e98e1d17445Chris Banes            if (hasNestedScrollingParent(ViewCompat.TYPE_NON_TOUCH)) {
154976daed103193a1756535d1f59b165e98e1d17445Chris Banes                stopNestedScroll(ViewCompat.TYPE_NON_TOUCH);
155076daed103193a1756535d1f59b165e98e1d17445Chris Banes            }
155176daed103193a1756535d1f59b165e98e1d17445Chris Banes            // and reset the scroller y
155276daed103193a1756535d1f59b165e98e1d17445Chris Banes            mLastScrollerY = 0;
15531fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
15541fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
15551fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
15561fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
15571fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * Scrolls the view to the given child.
15581fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *
15591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @param child the View to scroll to
15601fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
15611fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private void scrollToChild(View child) {
15621fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        child.getDrawingRect(mTempRect);
15631fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
15641fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        /* Offset from child's local coordinates to ScrollView coordinates */
15651fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        offsetDescendantRectToMyCoords(child, mTempRect);
15661fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
15671fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        int scrollDelta = computeScrollDeltaToGetChildRectOnScreen(mTempRect);
15681fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
15691fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (scrollDelta != 0) {
15701fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            scrollBy(0, scrollDelta);
15711fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
15721fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
15731fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
15741fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
15751fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * If rect is off screen, scroll just enough to get it (or at least the
15761fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * first screen size chunk of it) on screen.
15771fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *
15781fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @param rect      The rectangle.
15791fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @param immediate True to scroll immediately without animation
15801fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @return true if scrolling was performed
15811fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
15821fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private boolean scrollToChildRect(Rect rect, boolean immediate) {
15831fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        final int delta = computeScrollDeltaToGetChildRectOnScreen(rect);
15841fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        final boolean scroll = delta != 0;
15851fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (scroll) {
15861fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            if (immediate) {
15871fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                scrollBy(0, delta);
15881fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            } else {
15891fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                smoothScrollBy(0, delta);
15901fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
15911fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
15921fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return scroll;
15931fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
15941fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
15951fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
15961fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * Compute the amount to scroll in the Y direction in order to get
15971fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * a rectangle completely on the screen (or, if taller than the screen,
15981fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * at least the first screen size chunk of it).
15991fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *
16001fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @param rect The rect.
16011fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @return The scroll delta.
16021fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
16031fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    protected int computeScrollDeltaToGetChildRectOnScreen(Rect rect) {
16041fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (getChildCount() == 0) return 0;
16051fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
16061fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        int height = getHeight();
16071fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        int screenTop = getScrollY();
16081fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        int screenBottom = screenTop + height;
160993ade4a86ccf8e87af43401a3b3e41a804e311a1shepshapard        int actualScreenBottom = screenBottom;
16101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
16111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        int fadingEdge = getVerticalFadingEdgeLength();
16121fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
161393ade4a86ccf8e87af43401a3b3e41a804e311a1shepshapard        // TODO: screenTop should be incremented by fadingEdge * getTopFadingEdgeStrength (but for
161493ade4a86ccf8e87af43401a3b3e41a804e311a1shepshapard        // the target scroll distance).
16151fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        // leave room for top fading edge as long as rect isn't at very top
16161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (rect.top > 0) {
16171fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            screenTop += fadingEdge;
16181fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
16191fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
162093ade4a86ccf8e87af43401a3b3e41a804e311a1shepshapard        // TODO: screenBottom should be decremented by fadingEdge * getBottomFadingEdgeStrength (but
162193ade4a86ccf8e87af43401a3b3e41a804e311a1shepshapard        // for the target scroll distance).
16221fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        // leave room for bottom fading edge as long as rect isn't at very bottom
162393ade4a86ccf8e87af43401a3b3e41a804e311a1shepshapard        View child = getChildAt(0);
162493ade4a86ccf8e87af43401a3b3e41a804e311a1shepshapard        final NestedScrollView.LayoutParams lp = (LayoutParams) child.getLayoutParams();
162593ade4a86ccf8e87af43401a3b3e41a804e311a1shepshapard        if (rect.bottom < child.getHeight() + lp.topMargin + lp.bottomMargin) {
16261fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            screenBottom -= fadingEdge;
16271fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
16281fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
16291fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        int scrollYDelta = 0;
16301fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
16311fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (rect.bottom > screenBottom && rect.top > screenTop) {
16321fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            // need to move down to get it in view: move down just enough so
16331fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            // that the entire rectangle is in view (or at least the first
16341fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            // screen size chunk).
16351fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
16361fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            if (rect.height() > height) {
16371fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                // just enough to get screen size chunk on
16381fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                scrollYDelta += (rect.top - screenTop);
16391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            } else {
16401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                // get entire rect at bottom of screen
16411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                scrollYDelta += (rect.bottom - screenBottom);
16421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
16431fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
16441fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            // make sure we aren't scrolling beyond the end of our content
164593ade4a86ccf8e87af43401a3b3e41a804e311a1shepshapard            int bottom = child.getBottom() + lp.bottomMargin;
164693ade4a86ccf8e87af43401a3b3e41a804e311a1shepshapard            int distanceToBottom = bottom - actualScreenBottom;
16471fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            scrollYDelta = Math.min(scrollYDelta, distanceToBottom);
16481fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
16491fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        } else if (rect.top < screenTop && rect.bottom < screenBottom) {
16501fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            // need to move up to get it in view: move up just enough so that
16511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            // entire rectangle is in view (or at least the first screen
16521fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            // size chunk of it).
16531fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
16541fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            if (rect.height() > height) {
16551fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                // screen size chunk
16561fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                scrollYDelta -= (screenBottom - rect.bottom);
16571fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            } else {
16581fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                // entire rect at top
16591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                scrollYDelta -= (screenTop - rect.top);
16601fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
16611fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
16621fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            // make sure we aren't scrolling any further than the top our content
16631fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            scrollYDelta = Math.max(scrollYDelta, -getScrollY());
16641fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
16651fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return scrollYDelta;
16661fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
16671fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
16681fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
16691fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public void requestChildFocus(View child, View focused) {
16701fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (!mIsLayoutDirty) {
16711fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            scrollToChild(focused);
16721fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        } else {
16731fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            // The child may not be laid out yet, we can't compute the scroll yet
16741fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            mChildToScrollTo = focused;
16751fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
16761fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        super.requestChildFocus(child, focused);
16771fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
16781fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
16791fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
16801fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
16811fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * When looking for focus in children of a scroll view, need to be a little
16821fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * more careful not to give focus to something that is scrolled off screen.
16831fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *
16841fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * This is more expensive than the default {@link android.view.ViewGroup}
16851fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * implementation, otherwise this behavior might have been made the default.
16861fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
16871fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
16881fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    protected boolean onRequestFocusInDescendants(int direction,
16891fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            Rect previouslyFocusedRect) {
16901fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
16911fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        // convert from forward / backward notation to up / down / left / right
16921fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        // (ugh).
16931fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (direction == View.FOCUS_FORWARD) {
16941fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            direction = View.FOCUS_DOWN;
16951fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        } else if (direction == View.FOCUS_BACKWARD) {
16961fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            direction = View.FOCUS_UP;
16971fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
16981fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
16994d2c7b7c4f194034c5f17c4bee7320d808aabe4cAurimas Liutikas        final View nextFocus = previouslyFocusedRect == null
17004d2c7b7c4f194034c5f17c4bee7320d808aabe4cAurimas Liutikas                ? FocusFinder.getInstance().findNextFocus(this, null, direction)
17014d2c7b7c4f194034c5f17c4bee7320d808aabe4cAurimas Liutikas                : FocusFinder.getInstance().findNextFocusFromRect(
17024d2c7b7c4f194034c5f17c4bee7320d808aabe4cAurimas Liutikas                        this, previouslyFocusedRect, direction);
17031fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
17041fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (nextFocus == null) {
17051fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            return false;
17061fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
17071fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
17081fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (isOffScreen(nextFocus)) {
17091fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            return false;
17101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
17111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
17121fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return nextFocus.requestFocus(direction, previouslyFocusedRect);
17131fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
17141fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
17151fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
17161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public boolean requestChildRectangleOnScreen(View child, Rect rectangle,
17171fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            boolean immediate) {
17181fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        // offset into coordinate space of this scroll view
17191fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        rectangle.offset(child.getLeft() - child.getScrollX(),
17201fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                child.getTop() - child.getScrollY());
17211fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
17221fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return scrollToChildRect(rectangle, immediate);
17231fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
17241fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
17251fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
17261fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public void requestLayout() {
17271fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        mIsLayoutDirty = true;
17281fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        super.requestLayout();
17291fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
17301fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
17311fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
17321fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    protected void onLayout(boolean changed, int l, int t, int r, int b) {
17331fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        super.onLayout(changed, l, t, r, b);
17341fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        mIsLayoutDirty = false;
17351fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        // Give a child focus if it needs it
17361fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (mChildToScrollTo != null && isViewDescendantOf(mChildToScrollTo, this)) {
17371fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            scrollToChild(mChildToScrollTo);
17381fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
17391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        mChildToScrollTo = null;
17401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
17411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (!mIsLaidOut) {
174293ade4a86ccf8e87af43401a3b3e41a804e311a1shepshapard            // If there is a saved state, scroll to the position saved in that state.
17431fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            if (mSavedState != null) {
17441fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                scrollTo(getScrollX(), mSavedState.scrollPosition);
17451fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                mSavedState = null;
17461fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            } // mScrollY default value is "0"
17471fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
174893ade4a86ccf8e87af43401a3b3e41a804e311a1shepshapard            // Make sure current scrollY position falls into the scroll range.  If it doesn't,
174993ade4a86ccf8e87af43401a3b3e41a804e311a1shepshapard            // scroll such that it does.
175093ade4a86ccf8e87af43401a3b3e41a804e311a1shepshapard            int childSize = 0;
175193ade4a86ccf8e87af43401a3b3e41a804e311a1shepshapard            if (getChildCount() > 0) {
175293ade4a86ccf8e87af43401a3b3e41a804e311a1shepshapard                View child = getChildAt(0);
175393ade4a86ccf8e87af43401a3b3e41a804e311a1shepshapard                NestedScrollView.LayoutParams lp = (LayoutParams) child.getLayoutParams();
175493ade4a86ccf8e87af43401a3b3e41a804e311a1shepshapard                childSize = child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;
175593ade4a86ccf8e87af43401a3b3e41a804e311a1shepshapard            }
175693ade4a86ccf8e87af43401a3b3e41a804e311a1shepshapard            int parentSpace = b - t - getPaddingTop() - getPaddingBottom();
175793ade4a86ccf8e87af43401a3b3e41a804e311a1shepshapard            int currentScrollY = getScrollY();
175893ade4a86ccf8e87af43401a3b3e41a804e311a1shepshapard            int newScrollY = clamp(currentScrollY, parentSpace, childSize);
175993ade4a86ccf8e87af43401a3b3e41a804e311a1shepshapard            if (newScrollY != currentScrollY) {
176093ade4a86ccf8e87af43401a3b3e41a804e311a1shepshapard                scrollTo(getScrollX(), newScrollY);
17611fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
17621fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
17631fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
17641fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        // Calling this with the present values causes it to re-claim them
17651fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        scrollTo(getScrollX(), getScrollY());
17661fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        mIsLaidOut = true;
17671fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
17681fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
17691fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
17701fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public void onAttachedToWindow() {
1771d344e81ad956ec445038f5ba2ca2ebd1831b1f8aAlan Viverette        super.onAttachedToWindow();
1772d344e81ad956ec445038f5ba2ca2ebd1831b1f8aAlan Viverette
17731fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        mIsLaidOut = false;
17741fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
17751fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
17761fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
17771fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
17781fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        super.onSizeChanged(w, h, oldw, oldh);
17791fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
17801fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        View currentFocused = findFocus();
17814d2c7b7c4f194034c5f17c4bee7320d808aabe4cAurimas Liutikas        if (null == currentFocused || this == currentFocused) {
17821fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            return;
17834d2c7b7c4f194034c5f17c4bee7320d808aabe4cAurimas Liutikas        }
17841fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
17851fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        // If the currently-focused view was visible on the screen when the
17861fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        // screen was at the old height, then scroll the screen to make that
17871fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        // view visible with the new screen height.
17881fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (isWithinDeltaOfScreen(currentFocused, 0, oldh)) {
17891fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            currentFocused.getDrawingRect(mTempRect);
17901fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            offsetDescendantRectToMyCoords(currentFocused, mTempRect);
17911fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            int scrollDelta = computeScrollDeltaToGetChildRectOnScreen(mTempRect);
17921fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            doScrollY(scrollDelta);
17931fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
17941fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
17951fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
17961fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
17971fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * Return true if child is a descendant of parent, (or equal to the parent).
17981fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
17991fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private static boolean isViewDescendantOf(View child, View parent) {
18001fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (child == parent) {
18011fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            return true;
18021fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
18031fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
18041fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        final ViewParent theParent = child.getParent();
18051fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return (theParent instanceof ViewGroup) && isViewDescendantOf((View) theParent, parent);
18061fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
18071fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
18081fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
18091fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * Fling the scroll view
18101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *
18111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @param velocityY The initial velocity in the Y direction. Positive
18121fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *                  numbers mean that the finger/cursor is moving down the screen,
18131fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *                  which means we want to scroll towards the top.
18141fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
18151fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public void fling(int velocityY) {
18161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (getChildCount() > 0) {
181776daed103193a1756535d1f59b165e98e1d17445Chris Banes            startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL, ViewCompat.TYPE_NON_TOUCH);
181876daed103193a1756535d1f59b165e98e1d17445Chris Banes            mScroller.fling(getScrollX(), getScrollY(), // start
181976daed103193a1756535d1f59b165e98e1d17445Chris Banes                    0, velocityY, // velocities
182076daed103193a1756535d1f59b165e98e1d17445Chris Banes                    0, 0, // x
182176daed103193a1756535d1f59b165e98e1d17445Chris Banes                    Integer.MIN_VALUE, Integer.MAX_VALUE, // y
182276daed103193a1756535d1f59b165e98e1d17445Chris Banes                    0, 0); // overscroll
182376daed103193a1756535d1f59b165e98e1d17445Chris Banes            mLastScrollerY = getScrollY();
18241fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            ViewCompat.postInvalidateOnAnimation(this);
18251fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
18261fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
18271fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
18281fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private void flingWithNestedDispatch(int velocityY) {
18291fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        final int scrollY = getScrollY();
18304d2c7b7c4f194034c5f17c4bee7320d808aabe4cAurimas Liutikas        final boolean canFling = (scrollY > 0 || velocityY > 0)
18314d2c7b7c4f194034c5f17c4bee7320d808aabe4cAurimas Liutikas                && (scrollY < getScrollRange() || velocityY < 0);
18321fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (!dispatchNestedPreFling(0, velocityY)) {
18331fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            dispatchNestedFling(0, velocityY, canFling);
183476daed103193a1756535d1f59b165e98e1d17445Chris Banes            fling(velocityY);
18351fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
18361fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
18371fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
18381fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private void endDrag() {
18391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        mIsBeingDragged = false;
18401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
18411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        recycleVelocityTracker();
184276daed103193a1756535d1f59b165e98e1d17445Chris Banes        stopNestedScroll(ViewCompat.TYPE_TOUCH);
18431fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
18441fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (mEdgeGlowTop != null) {
18451fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            mEdgeGlowTop.onRelease();
18461fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            mEdgeGlowBottom.onRelease();
18471fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
18481fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
18491fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
18501fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
18511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * {@inheritDoc}
18521fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *
18531fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * <p>This version also clamps the scrolling to the bounds of our child.
18541fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
18551fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
18561fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public void scrollTo(int x, int y) {
18571fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        // we rely on the fact the View.scrollBy calls scrollTo.
18581fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (getChildCount() > 0) {
18591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            View child = getChildAt(0);
186093ade4a86ccf8e87af43401a3b3e41a804e311a1shepshapard            final NestedScrollView.LayoutParams lp = (LayoutParams) child.getLayoutParams();
186193ade4a86ccf8e87af43401a3b3e41a804e311a1shepshapard            int parentSpaceHorizontal = getWidth() - getPaddingLeft() - getPaddingRight();
186293ade4a86ccf8e87af43401a3b3e41a804e311a1shepshapard            int childSizeHorizontal = child.getWidth() + lp.leftMargin + lp.rightMargin;
186393ade4a86ccf8e87af43401a3b3e41a804e311a1shepshapard            int parentSpaceVertical = getHeight() - getPaddingTop() - getPaddingBottom();
186493ade4a86ccf8e87af43401a3b3e41a804e311a1shepshapard            int childSizeVertical = child.getHeight() + lp.topMargin + lp.bottomMargin;
186593ade4a86ccf8e87af43401a3b3e41a804e311a1shepshapard            x = clamp(x, parentSpaceHorizontal, childSizeHorizontal);
186693ade4a86ccf8e87af43401a3b3e41a804e311a1shepshapard            y = clamp(y, parentSpaceVertical, childSizeVertical);
18671fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            if (x != getScrollX() || y != getScrollY()) {
18681fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                super.scrollTo(x, y);
18691fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
18701fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
18711fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
18721fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
18731fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private void ensureGlows() {
1874e12dfa03641ad9cf0ddf272675bbe7d1198adbfdAurimas Liutikas        if (getOverScrollMode() != View.OVER_SCROLL_NEVER) {
18751fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            if (mEdgeGlowTop == null) {
18761fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                Context context = getContext();
1877506695b4cf6b22d70d2e3647d94551e721ee0430Aurimas Liutikas                mEdgeGlowTop = new EdgeEffect(context);
1878506695b4cf6b22d70d2e3647d94551e721ee0430Aurimas Liutikas                mEdgeGlowBottom = new EdgeEffect(context);
18791fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
18801fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        } else {
18811fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            mEdgeGlowTop = null;
18821fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            mEdgeGlowBottom = null;
18831fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
18841fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
18851fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
18861fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
18871fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public void draw(Canvas canvas) {
18881fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        super.draw(canvas);
18891fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (mEdgeGlowTop != null) {
18901fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            final int scrollY = getScrollY();
18911fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            if (!mEdgeGlowTop.isFinished()) {
18921fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                final int restoreCount = canvas.save();
1893bcb5323316d898cc22873efc2a145a5912e8a75cshepshapard                int width = getWidth();
1894bcb5323316d898cc22873efc2a145a5912e8a75cshepshapard                int height = getHeight();
1895bcb5323316d898cc22873efc2a145a5912e8a75cshepshapard                int xTranslation = 0;
1896bcb5323316d898cc22873efc2a145a5912e8a75cshepshapard                int yTranslation = Math.min(0, scrollY);
1897bcb5323316d898cc22873efc2a145a5912e8a75cshepshapard                if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP || getClipToPadding()) {
1898bcb5323316d898cc22873efc2a145a5912e8a75cshepshapard                    width -= getPaddingLeft() + getPaddingRight();
1899bcb5323316d898cc22873efc2a145a5912e8a75cshepshapard                    xTranslation += getPaddingLeft();
1900bcb5323316d898cc22873efc2a145a5912e8a75cshepshapard                }
1901bcb5323316d898cc22873efc2a145a5912e8a75cshepshapard                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && getClipToPadding()) {
1902bcb5323316d898cc22873efc2a145a5912e8a75cshepshapard                    height -= getPaddingTop() + getPaddingBottom();
1903bcb5323316d898cc22873efc2a145a5912e8a75cshepshapard                    yTranslation += getPaddingTop();
1904bcb5323316d898cc22873efc2a145a5912e8a75cshepshapard                }
1905bcb5323316d898cc22873efc2a145a5912e8a75cshepshapard                canvas.translate(xTranslation, yTranslation);
1906bcb5323316d898cc22873efc2a145a5912e8a75cshepshapard                mEdgeGlowTop.setSize(width, height);
19071fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                if (mEdgeGlowTop.draw(canvas)) {
19081fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    ViewCompat.postInvalidateOnAnimation(this);
19091fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                }
19101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                canvas.restoreToCount(restoreCount);
19111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
19121fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            if (!mEdgeGlowBottom.isFinished()) {
19131fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                final int restoreCount = canvas.save();
1914bcb5323316d898cc22873efc2a145a5912e8a75cshepshapard                int width = getWidth();
1915bcb5323316d898cc22873efc2a145a5912e8a75cshepshapard                int height = getHeight();
1916bcb5323316d898cc22873efc2a145a5912e8a75cshepshapard                int xTranslation = 0;
1917bcb5323316d898cc22873efc2a145a5912e8a75cshepshapard                int yTranslation = Math.max(getScrollRange(), scrollY) + height;
1918bcb5323316d898cc22873efc2a145a5912e8a75cshepshapard                if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP || getClipToPadding()) {
1919bcb5323316d898cc22873efc2a145a5912e8a75cshepshapard                    width -= getPaddingLeft() + getPaddingRight();
1920bcb5323316d898cc22873efc2a145a5912e8a75cshepshapard                    xTranslation += getPaddingLeft();
1921bcb5323316d898cc22873efc2a145a5912e8a75cshepshapard                }
1922bcb5323316d898cc22873efc2a145a5912e8a75cshepshapard                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && getClipToPadding()) {
1923bcb5323316d898cc22873efc2a145a5912e8a75cshepshapard                    height -= getPaddingTop() + getPaddingBottom();
1924bcb5323316d898cc22873efc2a145a5912e8a75cshepshapard                    yTranslation -= getPaddingBottom();
1925bcb5323316d898cc22873efc2a145a5912e8a75cshepshapard                }
1926bcb5323316d898cc22873efc2a145a5912e8a75cshepshapard                canvas.translate(xTranslation - width, yTranslation);
19271fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                canvas.rotate(180, width, 0);
19281fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                mEdgeGlowBottom.setSize(width, height);
19291fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                if (mEdgeGlowBottom.draw(canvas)) {
19301fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    ViewCompat.postInvalidateOnAnimation(this);
19311fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                }
19321fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                canvas.restoreToCount(restoreCount);
19331fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
19341fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
19351fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
19361fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
19371fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private static int clamp(int n, int my, int child) {
19381fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (my >= child || n < 0) {
19391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            /* my >= child is this case:
19401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell             *                    |--------------- me ---------------|
19411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell             *     |------ child ------|
19421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell             * or
19431fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell             *     |--------------- me ---------------|
19441fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell             *            |------ child ------|
19451fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell             * or
19461fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell             *     |--------------- me ---------------|
19471fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell             *                                  |------ child ------|
19481fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell             *
19491fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell             * n < 0 is this case:
19501fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell             *     |------ me ------|
19511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell             *                    |-------- child --------|
19521fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell             *     |-- mScrollX --|
19531fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell             */
19541fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            return 0;
19551fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
19564d2c7b7c4f194034c5f17c4bee7320d808aabe4cAurimas Liutikas        if ((my + n) > child) {
19571fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            /* this case:
19581fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell             *                    |------ me ------|
19591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell             *     |------ child ------|
19601fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell             *     |-- mScrollX --|
19611fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell             */
19624d2c7b7c4f194034c5f17c4bee7320d808aabe4cAurimas Liutikas            return child - my;
19631fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
19641fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return n;
19651fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
19661fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
19671fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
19681fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    protected void onRestoreInstanceState(Parcelable state) {
1969bb58a82daf11bf3c056d1cd5887aa26435d37b69Aurimas Liutikas        if (!(state instanceof SavedState)) {
1970bb58a82daf11bf3c056d1cd5887aa26435d37b69Aurimas Liutikas            super.onRestoreInstanceState(state);
1971bb58a82daf11bf3c056d1cd5887aa26435d37b69Aurimas Liutikas            return;
1972bb58a82daf11bf3c056d1cd5887aa26435d37b69Aurimas Liutikas        }
1973bb58a82daf11bf3c056d1cd5887aa26435d37b69Aurimas Liutikas
19741fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        SavedState ss = (SavedState) state;
19751fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        super.onRestoreInstanceState(ss.getSuperState());
19761fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        mSavedState = ss;
19771fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        requestLayout();
19781fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
19791fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
19801fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
19811fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    protected Parcelable onSaveInstanceState() {
19821fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        Parcelable superState = super.onSaveInstanceState();
19831fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        SavedState ss = new SavedState(superState);
19841fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        ss.scrollPosition = getScrollY();
19851fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return ss;
19861fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
19871fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
19881fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    static class SavedState extends BaseSavedState {
19891fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        public int scrollPosition;
19901fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
19911fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        SavedState(Parcelable superState) {
19921fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            super(superState);
19931fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
19941fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
19954d2c7b7c4f194034c5f17c4bee7320d808aabe4cAurimas Liutikas        SavedState(Parcel source) {
19961fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            super(source);
19971fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            scrollPosition = source.readInt();
19981fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
19991fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
20001fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        @Override
20011fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        public void writeToParcel(Parcel dest, int flags) {
20021fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            super.writeToParcel(dest, flags);
20031fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            dest.writeInt(scrollPosition);
20041fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
20051fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
20061fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        @Override
20071fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        public String toString() {
20081fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            return "HorizontalScrollView.SavedState{"
20091fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    + Integer.toHexString(System.identityHashCode(this))
20101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    + " scrollPosition=" + scrollPosition + "}";
20111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
20121fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
20134d2c7b7c4f194034c5f17c4bee7320d808aabe4cAurimas Liutikas        public static final Parcelable.Creator<SavedState> CREATOR =
20144d2c7b7c4f194034c5f17c4bee7320d808aabe4cAurimas Liutikas                new Parcelable.Creator<SavedState>() {
201515375aa6fd54b036f97f99229aefab2822c8a1c9Aurimas Liutikas            @Override
20161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            public SavedState createFromParcel(Parcel in) {
20171fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                return new SavedState(in);
20181fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
20191fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
202015375aa6fd54b036f97f99229aefab2822c8a1c9Aurimas Liutikas            @Override
20211fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            public SavedState[] newArray(int size) {
20221fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                return new SavedState[size];
20231fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
20241fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        };
20251fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
20261fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
20271fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    static class AccessibilityDelegate extends AccessibilityDelegateCompat {
20281fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        @Override
20291fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        public boolean performAccessibilityAction(View host, int action, Bundle arguments) {
20301fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            if (super.performAccessibilityAction(host, action, arguments)) {
20311fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                return true;
20321fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
20331fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            final NestedScrollView nsvHost = (NestedScrollView) host;
20341fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            if (!nsvHost.isEnabled()) {
20351fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                return false;
20361fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
20371fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            switch (action) {
20381fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                case AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD: {
20391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    final int viewportHeight = nsvHost.getHeight() - nsvHost.getPaddingBottom()
20401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                            - nsvHost.getPaddingTop();
20411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    final int targetScrollY = Math.min(nsvHost.getScrollY() + viewportHeight,
20421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                            nsvHost.getScrollRange());
20431fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    if (targetScrollY != nsvHost.getScrollY()) {
20441fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        nsvHost.smoothScrollTo(0, targetScrollY);
20451fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        return true;
20461fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    }
20471fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                }
20481fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                return false;
20491fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                case AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD: {
20501fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    final int viewportHeight = nsvHost.getHeight() - nsvHost.getPaddingBottom()
20511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                            - nsvHost.getPaddingTop();
20521fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    final int targetScrollY = Math.max(nsvHost.getScrollY() - viewportHeight, 0);
20531fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    if (targetScrollY != nsvHost.getScrollY()) {
20541fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        nsvHost.smoothScrollTo(0, targetScrollY);
20551fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        return true;
20561fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    }
20571fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                }
20581fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                return false;
20591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
20601fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            return false;
20611fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
20621fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
20631fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        @Override
20641fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) {
20651fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            super.onInitializeAccessibilityNodeInfo(host, info);
20661fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            final NestedScrollView nsvHost = (NestedScrollView) host;
20671fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            info.setClassName(ScrollView.class.getName());
20681fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            if (nsvHost.isEnabled()) {
20691fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                final int scrollRange = nsvHost.getScrollRange();
20701fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                if (scrollRange > 0) {
20711fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    info.setScrollable(true);
20721fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    if (nsvHost.getScrollY() > 0) {
20731fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD);
20741fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    }
20751fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    if (nsvHost.getScrollY() < scrollRange) {
20761fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD);
20771fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    }
20781fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                }
20791fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
20801fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
20811fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
20821fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        @Override
20831fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) {
20841fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            super.onInitializeAccessibilityEvent(host, event);
20851fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            final NestedScrollView nsvHost = (NestedScrollView) host;
20861fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            event.setClassName(ScrollView.class.getName());
20871fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            final boolean scrollable = nsvHost.getScrollRange() > 0;
208814d02ef06479168249fdfeea47bc105d05e88749Aurimas Liutikas            event.setScrollable(scrollable);
208914d02ef06479168249fdfeea47bc105d05e88749Aurimas Liutikas            event.setScrollX(nsvHost.getScrollX());
209014d02ef06479168249fdfeea47bc105d05e88749Aurimas Liutikas            event.setScrollY(nsvHost.getScrollY());
209114d02ef06479168249fdfeea47bc105d05e88749Aurimas Liutikas            AccessibilityRecordCompat.setMaxScrollX(event, nsvHost.getScrollX());
209214d02ef06479168249fdfeea47bc105d05e88749Aurimas Liutikas            AccessibilityRecordCompat.setMaxScrollY(event, nsvHost.getScrollRange());
20931fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
20941fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
20951fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell}
2096