NestedScrollView.java revision bcb5323316d898cc22873efc2a145a5912e8a75c
11fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell/*
21fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * Copyright (C) 2015 The Android Open Source Project
31fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell *
41fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * Licensed under the Apache License, Version 2.0 (the "License");
51fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * you may not use this file except in compliance with the License.
61fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * You may obtain a copy of the License at
71fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell *
81fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell *      http://www.apache.org/licenses/LICENSE-2.0
91fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell *
101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * Unless required by applicable law or agreed to in writing, software
111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * distributed under the License is distributed on an "AS IS" BASIS,
121fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
131fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * See the License for the specific language governing permissions and
141fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * limitations under the License.
151fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */
161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
171fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
181fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellpackage android.support.v4.widget;
191fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
208e10080c914d1ad0784394fa3026b85535535847Aurimas Liutikasimport static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
218e10080c914d1ad0784394fa3026b85535535847Aurimas Liutikas
221fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.content.Context;
231fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.content.res.TypedArray;
241fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.graphics.Canvas;
251fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.graphics.Rect;
26bcb5323316d898cc22873efc2a145a5912e8a75cshepshapardimport android.os.Build;
271fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.os.Bundle;
281fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.os.Parcel;
291fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.os.Parcelable;
30852749684a10e7d104eed8fb2dcc68b64d079340Jake Whartonimport android.support.annotation.NonNull;
31852749684a10e7d104eed8fb2dcc68b64d079340Jake Whartonimport android.support.annotation.Nullable;
32c39d9c75590eca86a5e7e32a8824ba04a0d42e9bAlan Viveretteimport android.support.annotation.RestrictTo;
331fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.support.v4.view.AccessibilityDelegateCompat;
341fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.support.v4.view.InputDeviceCompat;
3576daed103193a1756535d1f59b165e98e1d17445Chris Banesimport android.support.v4.view.NestedScrollingChild2;
361fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.support.v4.view.NestedScrollingChildHelper;
371fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.support.v4.view.NestedScrollingParent;
381fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.support.v4.view.NestedScrollingParentHelper;
39c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Arakiimport android.support.v4.view.ScrollingView;
401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.support.v4.view.ViewCompat;
411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.support.v4.view.accessibility.AccessibilityRecordCompat;
431fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.util.AttributeSet;
441fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.util.Log;
451fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.util.TypedValue;
461fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.view.FocusFinder;
471fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.view.KeyEvent;
481fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.view.MotionEvent;
491fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.view.VelocityTracker;
501fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.view.View;
511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.view.ViewConfiguration;
521fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.view.ViewGroup;
531fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.view.ViewParent;
541fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.view.accessibility.AccessibilityEvent;
551fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.view.animation.AnimationUtils;
56506695b4cf6b22d70d2e3647d94551e721ee0430Aurimas Liutikasimport android.widget.EdgeEffect;
571fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.widget.FrameLayout;
583035be16658d7652fdc472f971c81d8f7ffb60fdAurimas Liutikasimport android.widget.OverScroller;
591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport android.widget.ScrollView;
601fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
611fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellimport java.util.List;
621fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
631fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell/**
641fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * NestedScrollView is just like {@link android.widget.ScrollView}, but it supports acting
651fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * as both a nested scrolling parent and child on both new and old versions of Android.
661fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell * Nested scrolling is enabled by default.
671fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell */
681fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powellpublic class NestedScrollView extends FrameLayout implements NestedScrollingParent,
6976daed103193a1756535d1f59b165e98e1d17445Chris Banes        NestedScrollingChild2, ScrollingView {
701fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    static final int ANIMATED_SCROLL_GAP = 250;
711fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
721fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    static final float MAX_SCROLL_FACTOR = 0.5f;
731fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
741fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private static final String TAG = "NestedScrollView";
751fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
76cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes    /**
77cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes     * Interface definition for a callback to be invoked when the scroll
78cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes     * X or Y positions of a view change.
79cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes     *
80cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes     * <p>This version of the interface works on all versions of Android, back to API v4.</p>
81cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes     *
82cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes     * @see #setOnScrollChangeListener(OnScrollChangeListener)
83cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes     */
84cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes    public interface OnScrollChangeListener {
85cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes        /**
86cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes         * Called when the scroll position of a view changes.
87cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes         *
88cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes         * @param v The view whose scroll position has changed.
89cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes         * @param scrollX Current horizontal scroll origin.
90cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes         * @param scrollY Current vertical scroll origin.
91cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes         * @param oldScrollX Previous horizontal scroll origin.
92cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes         * @param oldScrollY Previous vertical scroll origin.
93cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes         */
94cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes        void onScrollChange(NestedScrollView v, int scrollX, int scrollY,
95cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes                int oldScrollX, int oldScrollY);
96cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes    }
97cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes
981fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private long mLastScroll;
991fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
1001fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private final Rect mTempRect = new Rect();
1013035be16658d7652fdc472f971c81d8f7ffb60fdAurimas Liutikas    private OverScroller mScroller;
102506695b4cf6b22d70d2e3647d94551e721ee0430Aurimas Liutikas    private EdgeEffect mEdgeGlowTop;
103506695b4cf6b22d70d2e3647d94551e721ee0430Aurimas Liutikas    private EdgeEffect mEdgeGlowBottom;
1041fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
1051fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
1061fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * Position of the last motion event.
1071fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
1081fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private int mLastMotionY;
1091fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
1101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
1111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * True when the layout has changed but the traversal has not come through yet.
1121fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * Ideally the view hierarchy would keep track of this for us.
1131fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
1141fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private boolean mIsLayoutDirty = true;
1151fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private boolean mIsLaidOut = false;
1161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
1171fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
1181fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * The child to give focus to in the event that a child has requested focus while the
1191fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * layout is dirty. This prevents the scroll from being wrong if the child has not been
1201fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * laid out before requesting focus.
1211fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
1221fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private View mChildToScrollTo = null;
1231fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
1241fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
1251fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * True if the user is currently dragging this ScrollView around. This is
1261fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * not the same as 'is being flinged', which can be checked by
1271fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * mScroller.isFinished() (flinging begins when the user lifts his finger).
1281fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
1291fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private boolean mIsBeingDragged = false;
1301fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
1311fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
1321fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * Determines speed during touch scrolling
1331fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
1341fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private VelocityTracker mVelocityTracker;
1351fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
1361fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
1371fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * When set to true, the scroll view measure its child to make it fill the currently
1381fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * visible area.
1391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
1401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private boolean mFillViewport;
1411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
1421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
1431fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * Whether arrow scrolling is animated.
1441fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
1451fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private boolean mSmoothScrollingEnabled = true;
1461fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
1471fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private int mTouchSlop;
1481fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private int mMinimumVelocity;
1491fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private int mMaximumVelocity;
1501fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
1511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
1521fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * ID of the active pointer. This is used to retain consistency during
1531fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * drags/flings if multiple pointers are used.
1541fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
1551fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private int mActivePointerId = INVALID_POINTER;
1561fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
1571fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
1581fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * Used during scrolling to retrieve the new offset within the window.
1591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
1601fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private final int[] mScrollOffset = new int[2];
1611fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private final int[] mScrollConsumed = new int[2];
1621fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private int mNestedYOffset;
1631fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
16476daed103193a1756535d1f59b165e98e1d17445Chris Banes    private int mLastScrollerY;
16576daed103193a1756535d1f59b165e98e1d17445Chris Banes
1661fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
1671fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * Sentinel value for no current active pointer.
1681fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * Used by {@link #mActivePointerId}.
1691fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
1701fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private static final int INVALID_POINTER = -1;
1711fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
1721fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private SavedState mSavedState;
1731fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
1741fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private static final AccessibilityDelegate ACCESSIBILITY_DELEGATE = new AccessibilityDelegate();
1751fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
1761fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private static final int[] SCROLLVIEW_STYLEABLE = new int[] {
1771fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            android.R.attr.fillViewport
1781fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    };
1791fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
1801fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private final NestedScrollingParentHelper mParentHelper;
1811fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private final NestedScrollingChildHelper mChildHelper;
1821fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
1831fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private float mVerticalScrollFactor;
1841fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
185cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes    private OnScrollChangeListener mOnScrollChangeListener;
186cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes
187852749684a10e7d104eed8fb2dcc68b64d079340Jake Wharton    public NestedScrollView(@NonNull Context context) {
1881fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        this(context, null);
1891fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
1901fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
191852749684a10e7d104eed8fb2dcc68b64d079340Jake Wharton    public NestedScrollView(@NonNull Context context, @Nullable AttributeSet attrs) {
1921fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        this(context, attrs, 0);
1931fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
1941fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
195852749684a10e7d104eed8fb2dcc68b64d079340Jake Wharton    public NestedScrollView(@NonNull Context context, @Nullable AttributeSet attrs,
196852749684a10e7d104eed8fb2dcc68b64d079340Jake Wharton            int defStyleAttr) {
1971fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        super(context, attrs, defStyleAttr);
1981fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        initScrollView();
1991fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
2001fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        final TypedArray a = context.obtainStyledAttributes(
2011fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                attrs, SCROLLVIEW_STYLEABLE, defStyleAttr, 0);
2021fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
2031fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        setFillViewport(a.getBoolean(0, false));
2041fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
2051fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        a.recycle();
2061fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
2071fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        mParentHelper = new NestedScrollingParentHelper(this);
2081fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        mChildHelper = new NestedScrollingChildHelper(this);
2091fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
2101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        // ...because why else would you be using this widget?
2111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        setNestedScrollingEnabled(true);
2121fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
2131fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        ViewCompat.setAccessibilityDelegate(this, ACCESSIBILITY_DELEGATE);
2141fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
2151fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
2161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    // NestedScrollingChild
2171fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
2181fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
2191fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public void setNestedScrollingEnabled(boolean enabled) {
2201fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        mChildHelper.setNestedScrollingEnabled(enabled);
2211fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
2221fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
2231fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
2241fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public boolean isNestedScrollingEnabled() {
2251fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return mChildHelper.isNestedScrollingEnabled();
2261fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
2271fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
2281fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
2291fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public boolean startNestedScroll(int axes) {
2301fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return mChildHelper.startNestedScroll(axes);
2311fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
2321fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
2331fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
23476daed103193a1756535d1f59b165e98e1d17445Chris Banes    public boolean startNestedScroll(int axes, int type) {
23576daed103193a1756535d1f59b165e98e1d17445Chris Banes        return mChildHelper.startNestedScroll(axes, type);
23676daed103193a1756535d1f59b165e98e1d17445Chris Banes    }
23776daed103193a1756535d1f59b165e98e1d17445Chris Banes
23876daed103193a1756535d1f59b165e98e1d17445Chris Banes    @Override
2391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public void stopNestedScroll() {
2401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        mChildHelper.stopNestedScroll();
2411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
2421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
2431fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
24476daed103193a1756535d1f59b165e98e1d17445Chris Banes    public void stopNestedScroll(int type) {
24576daed103193a1756535d1f59b165e98e1d17445Chris Banes        mChildHelper.stopNestedScroll(type);
24676daed103193a1756535d1f59b165e98e1d17445Chris Banes    }
24776daed103193a1756535d1f59b165e98e1d17445Chris Banes
24876daed103193a1756535d1f59b165e98e1d17445Chris Banes    @Override
2491fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public boolean hasNestedScrollingParent() {
2501fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return mChildHelper.hasNestedScrollingParent();
2511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
2521fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
2531fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
25476daed103193a1756535d1f59b165e98e1d17445Chris Banes    public boolean hasNestedScrollingParent(int type) {
25576daed103193a1756535d1f59b165e98e1d17445Chris Banes        return mChildHelper.hasNestedScrollingParent(type);
25676daed103193a1756535d1f59b165e98e1d17445Chris Banes    }
25776daed103193a1756535d1f59b165e98e1d17445Chris Banes
25876daed103193a1756535d1f59b165e98e1d17445Chris Banes    @Override
2591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed,
2601fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            int dyUnconsumed, int[] offsetInWindow) {
2611fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return mChildHelper.dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed,
2621fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                offsetInWindow);
2631fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
2641fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
2651fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
26676daed103193a1756535d1f59b165e98e1d17445Chris Banes    public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed,
26776daed103193a1756535d1f59b165e98e1d17445Chris Banes            int dyUnconsumed, int[] offsetInWindow, int type) {
26876daed103193a1756535d1f59b165e98e1d17445Chris Banes        return mChildHelper.dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed,
26976daed103193a1756535d1f59b165e98e1d17445Chris Banes                offsetInWindow, type);
27076daed103193a1756535d1f59b165e98e1d17445Chris Banes    }
27176daed103193a1756535d1f59b165e98e1d17445Chris Banes
27276daed103193a1756535d1f59b165e98e1d17445Chris Banes    @Override
2731fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) {
2741fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return mChildHelper.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow);
2751fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
2761fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
2771fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
27876daed103193a1756535d1f59b165e98e1d17445Chris Banes    public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow,
27976daed103193a1756535d1f59b165e98e1d17445Chris Banes            int type) {
28076daed103193a1756535d1f59b165e98e1d17445Chris Banes        return mChildHelper.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow, type);
28176daed103193a1756535d1f59b165e98e1d17445Chris Banes    }
28276daed103193a1756535d1f59b165e98e1d17445Chris Banes
28376daed103193a1756535d1f59b165e98e1d17445Chris Banes    @Override
2841fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) {
2851fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return mChildHelper.dispatchNestedFling(velocityX, velocityY, consumed);
2861fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
2871fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
2881fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
2891fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public boolean dispatchNestedPreFling(float velocityX, float velocityY) {
2901fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return mChildHelper.dispatchNestedPreFling(velocityX, velocityY);
2911fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
2921fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
2931fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    // NestedScrollingParent
2941fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
2951fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
2961fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) {
2971fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0;
2981fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
2991fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
3001fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
3011fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public void onNestedScrollAccepted(View child, View target, int nestedScrollAxes) {
3021fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        mParentHelper.onNestedScrollAccepted(child, target, nestedScrollAxes);
3031fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL);
3041fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
3051fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
3061fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
3071fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public void onStopNestedScroll(View target) {
308cb3b6124a87f9116ce92fcf17f59287445c41442Chris Banes        mParentHelper.onStopNestedScroll(target);
3091fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        stopNestedScroll();
3101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
3111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
3121fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
3131fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed,
3141fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            int dyUnconsumed) {
3151fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        final int oldScrollY = getScrollY();
3161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        scrollBy(0, dyUnconsumed);
3171fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        final int myConsumed = getScrollY() - oldScrollY;
3181fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        final int myUnconsumed = dyUnconsumed - myConsumed;
3191fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        dispatchNestedScroll(0, myConsumed, 0, myUnconsumed, null);
3201fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
3211fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
3221fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
3231fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) {
324d370f75e50bf31d348d4e424ba8477ac77635f70Chris Banes        dispatchNestedPreScroll(dx, dy, consumed, null);
3251fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
3261fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
3271fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
3281fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) {
3291fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (!consumed) {
3301fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            flingWithNestedDispatch((int) velocityY);
3311fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            return true;
3321fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
3331fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return false;
3341fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
3351fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
3361fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
3371fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public boolean onNestedPreFling(View target, float velocityX, float velocityY) {
338d370f75e50bf31d348d4e424ba8477ac77635f70Chris Banes        return dispatchNestedPreFling(velocityX, velocityY);
3391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
3401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
3411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
3421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public int getNestedScrollAxes() {
3431fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return mParentHelper.getNestedScrollAxes();
3441fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
3451fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
3461fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    // ScrollView import
3471fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
348e2104f4b5c8e3ad63570306a25e61502dfe4c418Aurimas Liutikas    @Override
3491fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public boolean shouldDelayChildPressedState() {
3501fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return true;
3511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
3521fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
3531fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
3541fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    protected float getTopFadingEdgeStrength() {
3551fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (getChildCount() == 0) {
3561fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            return 0.0f;
3571fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
3581fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
3591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        final int length = getVerticalFadingEdgeLength();
3601fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        final int scrollY = getScrollY();
3611fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (scrollY < length) {
3621fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            return scrollY / (float) length;
3631fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
3641fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
3651fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return 1.0f;
3661fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
3671fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
3681fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
3691fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    protected float getBottomFadingEdgeStrength() {
3701fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (getChildCount() == 0) {
3711fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            return 0.0f;
3721fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
3731fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
3741fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        final int length = getVerticalFadingEdgeLength();
3751fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        final int bottomEdge = getHeight() - getPaddingBottom();
3761fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        final int span = getChildAt(0).getBottom() - getScrollY() - bottomEdge;
3771fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (span < length) {
3781fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            return span / (float) length;
3791fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
3801fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
3811fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return 1.0f;
3821fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
3831fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
3841fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
3851fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @return The maximum amount this scroll view will scroll in response to
3861fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *   an arrow event.
3871fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
3881fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public int getMaxScrollAmount() {
3891fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return (int) (MAX_SCROLL_FACTOR * getHeight());
3901fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
3911fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
3921fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private void initScrollView() {
3933035be16658d7652fdc472f971c81d8f7ffb60fdAurimas Liutikas        mScroller = new OverScroller(getContext());
3941fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        setFocusable(true);
3951fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
3961fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        setWillNotDraw(false);
3971fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        final ViewConfiguration configuration = ViewConfiguration.get(getContext());
3981fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        mTouchSlop = configuration.getScaledTouchSlop();
3991fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
4001fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
4011fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
4021fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
4031fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
4041fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public void addView(View child) {
4051fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (getChildCount() > 0) {
4061fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            throw new IllegalStateException("ScrollView can host only one direct child");
4071fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
4081fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
4091fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        super.addView(child);
4101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
4111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
4121fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
4131fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public void addView(View child, int index) {
4141fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (getChildCount() > 0) {
4151fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            throw new IllegalStateException("ScrollView can host only one direct child");
4161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
4171fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
4181fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        super.addView(child, index);
4191fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
4201fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
4211fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
4221fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public void addView(View child, ViewGroup.LayoutParams params) {
4231fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (getChildCount() > 0) {
4241fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            throw new IllegalStateException("ScrollView can host only one direct child");
4251fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
4261fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
4271fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        super.addView(child, params);
4281fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
4291fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
4301fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
4311fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public void addView(View child, int index, ViewGroup.LayoutParams params) {
4321fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (getChildCount() > 0) {
4331fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            throw new IllegalStateException("ScrollView can host only one direct child");
4341fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
4351fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
4361fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        super.addView(child, index, params);
4371fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
4381fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
4391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
440cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes     * Register a callback to be invoked when the scroll X or Y positions of
441cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes     * this view change.
442cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes     * <p>This version of the method works on all versions of Android, back to API v4.</p>
443cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes     *
444cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes     * @param l The listener to notify when the scroll X or Y position changes.
445cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes     * @see android.view.View#getScrollX()
446cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes     * @see android.view.View#getScrollY()
447cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes     */
448852749684a10e7d104eed8fb2dcc68b64d079340Jake Wharton    public void setOnScrollChangeListener(@Nullable OnScrollChangeListener l) {
449cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes        mOnScrollChangeListener = l;
450cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes    }
451cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes
452cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes    /**
4531fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @return Returns true this ScrollView can be scrolled
4541fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
4551fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private boolean canScroll() {
4561fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        View child = getChildAt(0);
4571fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (child != null) {
4581fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            int childHeight = child.getHeight();
4591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            return getHeight() < childHeight + getPaddingTop() + getPaddingBottom();
4601fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
4611fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return false;
4621fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
4631fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
4641fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
4651fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * Indicates whether this ScrollView's content is stretched to fill the viewport.
4661fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *
4671fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @return True if the content fills the viewport, false otherwise.
4681fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *
469929f27aab7ac7231f3734c988d5ee7201627d535Alan Viverette     * @attr name android:fillViewport
4701fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
4711fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public boolean isFillViewport() {
4721fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return mFillViewport;
4731fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
4741fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
4751fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
476236bf5bc5041e4bf356eac3855fd4fcf72cd6994Aurimas Liutikas     * Set whether this ScrollView should stretch its content height to fill the viewport or not.
4771fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *
4781fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @param fillViewport True to stretch the content's height to the viewport's
4791fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *        boundaries, false otherwise.
4801fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *
481929f27aab7ac7231f3734c988d5ee7201627d535Alan Viverette     * @attr name android:fillViewport
4821fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
4831fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public void setFillViewport(boolean fillViewport) {
4841fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (fillViewport != mFillViewport) {
4851fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            mFillViewport = fillViewport;
4861fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            requestLayout();
4871fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
4881fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
4891fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
4901fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
4911fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @return Whether arrow scrolling will animate its transition.
4921fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
4931fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public boolean isSmoothScrollingEnabled() {
4941fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return mSmoothScrollingEnabled;
4951fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
4961fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
4971fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
4981fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * Set whether arrow scrolling will animate its transition.
4991fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @param smoothScrollingEnabled whether arrow scrolling will animate its transition
5001fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
5011fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public void setSmoothScrollingEnabled(boolean smoothScrollingEnabled) {
5021fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        mSmoothScrollingEnabled = smoothScrollingEnabled;
5031fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
5041fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
5051fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
506cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
507cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes        super.onScrollChanged(l, t, oldl, oldt);
508cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes
509cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes        if (mOnScrollChangeListener != null) {
510cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes            mOnScrollChangeListener.onScrollChange(this, l, t, oldl, oldt);
511cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes        }
512cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes    }
513cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes
514cc82b3ee0c53b2f2da75b7fda5fe4b8f5af5dc1cChris Banes    @Override
5151fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
5161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
5171fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
5181fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (!mFillViewport) {
5191fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            return;
5201fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
5211fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
5221fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
5231fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (heightMode == MeasureSpec.UNSPECIFIED) {
5241fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            return;
5251fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
5261fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
5271fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (getChildCount() > 0) {
5281fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            final View child = getChildAt(0);
5291fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            int height = getMeasuredHeight();
5301fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            if (child.getMeasuredHeight() < height) {
5311fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                final FrameLayout.LayoutParams lp = (LayoutParams) child.getLayoutParams();
5321fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
5331fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                int childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
5341fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        getPaddingLeft() + getPaddingRight(), lp.width);
5351fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                height -= getPaddingTop();
5361fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                height -= getPaddingBottom();
5371fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                int childHeightMeasureSpec =
5381fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
5391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
5401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
5411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
5421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
5431fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
5441fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
5451fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
5461fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public boolean dispatchKeyEvent(KeyEvent event) {
5471fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        // Let the focused view and/or our descendants get the key first
5481fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return super.dispatchKeyEvent(event) || executeKeyEvent(event);
5491fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
5501fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
5511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
5521fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * You can call this function yourself to have the scroll view perform
5531fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * scrolling from a key event, just as if the event had been dispatched to
5541fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * it by the view hierarchy.
5551fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *
5561fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @param event The key event to execute.
5571fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @return Return true if the event was handled, else false.
5581fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
559852749684a10e7d104eed8fb2dcc68b64d079340Jake Wharton    public boolean executeKeyEvent(@NonNull KeyEvent event) {
5601fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        mTempRect.setEmpty();
5611fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
5621fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (!canScroll()) {
5631fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            if (isFocused() && event.getKeyCode() != KeyEvent.KEYCODE_BACK) {
5641fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                View currentFocused = findFocus();
5651fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                if (currentFocused == this) currentFocused = null;
5661fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                View nextFocused = FocusFinder.getInstance().findNextFocus(this,
5671fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        currentFocused, View.FOCUS_DOWN);
5681fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                return nextFocused != null
5691fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        && nextFocused != this
5701fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        && nextFocused.requestFocus(View.FOCUS_DOWN);
5711fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
5721fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            return false;
5731fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
5741fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
5751fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        boolean handled = false;
5761fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (event.getAction() == KeyEvent.ACTION_DOWN) {
5771fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            switch (event.getKeyCode()) {
5781fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                case KeyEvent.KEYCODE_DPAD_UP:
5791fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    if (!event.isAltPressed()) {
5801fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        handled = arrowScroll(View.FOCUS_UP);
5811fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    } else {
5821fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        handled = fullScroll(View.FOCUS_UP);
5831fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    }
5841fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    break;
5851fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                case KeyEvent.KEYCODE_DPAD_DOWN:
5861fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    if (!event.isAltPressed()) {
5871fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        handled = arrowScroll(View.FOCUS_DOWN);
5881fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    } else {
5891fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        handled = fullScroll(View.FOCUS_DOWN);
5901fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    }
5911fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    break;
5921fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                case KeyEvent.KEYCODE_SPACE:
5931fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    pageScroll(event.isShiftPressed() ? View.FOCUS_UP : View.FOCUS_DOWN);
5941fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    break;
5951fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
5961fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
5971fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
5981fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return handled;
5991fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
6001fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
6011fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private boolean inChild(int x, int y) {
6021fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (getChildCount() > 0) {
6031fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            final int scrollY = getScrollY();
6041fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            final View child = getChildAt(0);
6051fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            return !(y < child.getTop() - scrollY
6061fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    || y >= child.getBottom() - scrollY
6071fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    || x < child.getLeft()
6081fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    || x >= child.getRight());
6091fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
6101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return false;
6111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
6121fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
6131fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private void initOrResetVelocityTracker() {
6141fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (mVelocityTracker == null) {
6151fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            mVelocityTracker = VelocityTracker.obtain();
6161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        } else {
6171fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            mVelocityTracker.clear();
6181fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
6191fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
6201fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
6211fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private void initVelocityTrackerIfNotExists() {
6221fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (mVelocityTracker == null) {
6231fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            mVelocityTracker = VelocityTracker.obtain();
6241fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
6251fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
6261fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
6271fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private void recycleVelocityTracker() {
6281fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (mVelocityTracker != null) {
6291fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            mVelocityTracker.recycle();
6301fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            mVelocityTracker = null;
6311fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
6321fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
6331fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
6341fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
6351fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
6361fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (disallowIntercept) {
6371fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            recycleVelocityTracker();
6381fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
6391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        super.requestDisallowInterceptTouchEvent(disallowIntercept);
6401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
6411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
6421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
6431fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public boolean onInterceptTouchEvent(MotionEvent ev) {
6441fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        /*
6451fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell         * This method JUST determines whether we want to intercept the motion.
6461fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell         * If we return true, onMotionEvent will be called and we do the actual
6471fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell         * scrolling there.
6481fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell         */
6491fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
6501fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        /*
6511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        * Shortcut the most recurring case: the user is in the dragging
6521fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        * state and he is moving his finger.  We want to intercept this
6531fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        * motion.
6541fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        */
6551fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        final int action = ev.getAction();
6561fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if ((action == MotionEvent.ACTION_MOVE) && (mIsBeingDragged)) {
6571fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            return true;
6581fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
6591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
6606ed40c1f86bcb172a1f0f069cde1c571a7781aeeAurimas Liutikas        switch (action & MotionEvent.ACTION_MASK) {
6611fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            case MotionEvent.ACTION_MOVE: {
6621fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                /*
6631fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                 * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check
6641fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                 * whether the user has moved far enough from his original down touch.
6651fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                 */
6661fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
6671fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                /*
6681fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                * Locally do absolute value. mLastMotionY is set to the y value
6691fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                * of the down event.
6701fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                */
6711fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                final int activePointerId = mActivePointerId;
6721fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                if (activePointerId == INVALID_POINTER) {
6731fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    // If we don't have a valid id, the touch down wasn't on content.
6741fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    break;
6751fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                }
6761fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
6771b3e9466b4c4d72f28bb4448672ef8bab19b6f3eKirill Grouchnikov                final int pointerIndex = ev.findPointerIndex(activePointerId);
6781fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                if (pointerIndex == -1) {
6791fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    Log.e(TAG, "Invalid pointerId=" + activePointerId
6801fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                            + " in onInterceptTouchEvent");
6811fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    break;
6821fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                }
6831fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
6841b3e9466b4c4d72f28bb4448672ef8bab19b6f3eKirill Grouchnikov                final int y = (int) ev.getY(pointerIndex);
6851fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                final int yDiff = Math.abs(y - mLastMotionY);
6861fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                if (yDiff > mTouchSlop
6871fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        && (getNestedScrollAxes() & ViewCompat.SCROLL_AXIS_VERTICAL) == 0) {
6881fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    mIsBeingDragged = true;
6891fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    mLastMotionY = y;
6901fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    initVelocityTrackerIfNotExists();
6911fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    mVelocityTracker.addMovement(ev);
6921fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    mNestedYOffset = 0;
6931fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    final ViewParent parent = getParent();
6941fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    if (parent != null) {
6951fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        parent.requestDisallowInterceptTouchEvent(true);
6961fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    }
6971fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                }
6981fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                break;
6991fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
7001fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
7011fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            case MotionEvent.ACTION_DOWN: {
7021fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                final int y = (int) ev.getY();
70376daed103193a1756535d1f59b165e98e1d17445Chris Banes                if (!inChild((int) ev.getX(), y)) {
7041fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    mIsBeingDragged = false;
7051fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    recycleVelocityTracker();
7061fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    break;
7071fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                }
7081fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
7091fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                /*
7101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                 * Remember location of down touch.
7111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                 * ACTION_DOWN always refers to pointer index 0.
7121fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                 */
7131fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                mLastMotionY = y;
7141b3e9466b4c4d72f28bb4448672ef8bab19b6f3eKirill Grouchnikov                mActivePointerId = ev.getPointerId(0);
7151fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
7161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                initOrResetVelocityTracker();
7171fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                mVelocityTracker.addMovement(ev);
7181fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                /*
719661d25de05417a357540283ee0cd93b1326ac3a4Chris Banes                 * If being flinged and user touches the screen, initiate drag;
720661d25de05417a357540283ee0cd93b1326ac3a4Chris Banes                 * otherwise don't. mScroller.isFinished should be false when
721661d25de05417a357540283ee0cd93b1326ac3a4Chris Banes                 * being flinged. We need to call computeScrollOffset() first so that
722661d25de05417a357540283ee0cd93b1326ac3a4Chris Banes                 * isFinished() is correct.
7231fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                */
724661d25de05417a357540283ee0cd93b1326ac3a4Chris Banes                mScroller.computeScrollOffset();
7251fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                mIsBeingDragged = !mScroller.isFinished();
72676daed103193a1756535d1f59b165e98e1d17445Chris Banes                startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL, ViewCompat.TYPE_TOUCH);
7271fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                break;
7281fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
7291fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
7301fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            case MotionEvent.ACTION_CANCEL:
7311fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            case MotionEvent.ACTION_UP:
7321fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                /* Release the drag */
7331fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                mIsBeingDragged = false;
7341fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                mActivePointerId = INVALID_POINTER;
7351fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                recycleVelocityTracker();
73600db92e217c3bc08acd09143cac8e3d3b0d0e813Chris Banes                if (mScroller.springBack(getScrollX(), getScrollY(), 0, 0, 0, getScrollRange())) {
73700db92e217c3bc08acd09143cac8e3d3b0d0e813Chris Banes                    ViewCompat.postInvalidateOnAnimation(this);
73800db92e217c3bc08acd09143cac8e3d3b0d0e813Chris Banes                }
73976daed103193a1756535d1f59b165e98e1d17445Chris Banes                stopNestedScroll(ViewCompat.TYPE_TOUCH);
7401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                break;
7416ed40c1f86bcb172a1f0f069cde1c571a7781aeeAurimas Liutikas            case MotionEvent.ACTION_POINTER_UP:
7421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                onSecondaryPointerUp(ev);
7431fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                break;
7441fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
7451fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
7461fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        /*
7471fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        * The only time we want to intercept motion events is if we are in the
7481fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        * drag mode.
7491fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        */
7501fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return mIsBeingDragged;
7511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
7521fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
7531fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
7541fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public boolean onTouchEvent(MotionEvent ev) {
7551fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        initVelocityTrackerIfNotExists();
7561fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
7571fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        MotionEvent vtev = MotionEvent.obtain(ev);
7581fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
7596ed40c1f86bcb172a1f0f069cde1c571a7781aeeAurimas Liutikas        final int actionMasked = ev.getActionMasked();
7601fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
7611fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (actionMasked == MotionEvent.ACTION_DOWN) {
7621fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            mNestedYOffset = 0;
7631fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
7641fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        vtev.offsetLocation(0, mNestedYOffset);
7651fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
7661fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        switch (actionMasked) {
7671fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            case MotionEvent.ACTION_DOWN: {
7681fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                if (getChildCount() == 0) {
7691fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    return false;
7701fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                }
7711fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                if ((mIsBeingDragged = !mScroller.isFinished())) {
7721fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    final ViewParent parent = getParent();
7731fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    if (parent != null) {
7741fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        parent.requestDisallowInterceptTouchEvent(true);
7751fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    }
7761fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                }
7771fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
7781fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                /*
7791fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                 * If being flinged and user touches, stop the fling. isFinished
7801fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                 * will be false if being flinged.
7811fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                 */
7821fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                if (!mScroller.isFinished()) {
7831fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    mScroller.abortAnimation();
7841fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                }
7851fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
7861fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                // Remember where the motion event started
7871fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                mLastMotionY = (int) ev.getY();
7881b3e9466b4c4d72f28bb4448672ef8bab19b6f3eKirill Grouchnikov                mActivePointerId = ev.getPointerId(0);
78976daed103193a1756535d1f59b165e98e1d17445Chris Banes                startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL, ViewCompat.TYPE_TOUCH);
7901fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                break;
7911fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
7921fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            case MotionEvent.ACTION_MOVE:
7931b3e9466b4c4d72f28bb4448672ef8bab19b6f3eKirill Grouchnikov                final int activePointerIndex = ev.findPointerIndex(mActivePointerId);
7941fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                if (activePointerIndex == -1) {
7951fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    Log.e(TAG, "Invalid pointerId=" + mActivePointerId + " in onTouchEvent");
7961fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    break;
7971fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                }
7981fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
7991b3e9466b4c4d72f28bb4448672ef8bab19b6f3eKirill Grouchnikov                final int y = (int) ev.getY(activePointerIndex);
8001fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                int deltaY = mLastMotionY - y;
80176daed103193a1756535d1f59b165e98e1d17445Chris Banes                if (dispatchNestedPreScroll(0, deltaY, mScrollConsumed, mScrollOffset,
80276daed103193a1756535d1f59b165e98e1d17445Chris Banes                        ViewCompat.TYPE_TOUCH)) {
8031fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    deltaY -= mScrollConsumed[1];
8041fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    vtev.offsetLocation(0, mScrollOffset[1]);
8051fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    mNestedYOffset += mScrollOffset[1];
8061fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                }
8071fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                if (!mIsBeingDragged && Math.abs(deltaY) > mTouchSlop) {
8081fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    final ViewParent parent = getParent();
8091fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    if (parent != null) {
8101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        parent.requestDisallowInterceptTouchEvent(true);
8111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    }
8121fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    mIsBeingDragged = true;
8131fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    if (deltaY > 0) {
8141fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        deltaY -= mTouchSlop;
8151fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    } else {
8161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        deltaY += mTouchSlop;
8171fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    }
8181fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                }
8191fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                if (mIsBeingDragged) {
8201fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    // Scroll to follow the motion event
8211fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    mLastMotionY = y - mScrollOffset[1];
8221fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
8231fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    final int oldY = getScrollY();
8241fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    final int range = getScrollRange();
825e12dfa03641ad9cf0ddf272675bbe7d1198adbfdAurimas Liutikas                    final int overscrollMode = getOverScrollMode();
826e12dfa03641ad9cf0ddf272675bbe7d1198adbfdAurimas Liutikas                    boolean canOverscroll = overscrollMode == View.OVER_SCROLL_ALWAYS
827e12dfa03641ad9cf0ddf272675bbe7d1198adbfdAurimas Liutikas                            || (overscrollMode == View.OVER_SCROLL_IF_CONTENT_SCROLLS && range > 0);
8281fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
8291fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    // Calling overScrollByCompat will call onOverScrolled, which
8301fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    // calls onScrollChanged if applicable.
8311fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    if (overScrollByCompat(0, deltaY, 0, getScrollY(), 0, range, 0,
83276daed103193a1756535d1f59b165e98e1d17445Chris Banes                            0, true) && !hasNestedScrollingParent(ViewCompat.TYPE_TOUCH)) {
8331fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        // Break our velocity if we hit a scroll barrier.
8341fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        mVelocityTracker.clear();
8351fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    }
8361fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
8371fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    final int scrolledDeltaY = getScrollY() - oldY;
8381fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    final int unconsumedY = deltaY - scrolledDeltaY;
83976daed103193a1756535d1f59b165e98e1d17445Chris Banes                    if (dispatchNestedScroll(0, scrolledDeltaY, 0, unconsumedY, mScrollOffset,
84076daed103193a1756535d1f59b165e98e1d17445Chris Banes                            ViewCompat.TYPE_TOUCH)) {
8411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        mLastMotionY -= mScrollOffset[1];
8421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        vtev.offsetLocation(0, mScrollOffset[1]);
8431fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        mNestedYOffset += mScrollOffset[1];
8441fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    } else if (canOverscroll) {
8451fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        ensureGlows();
8461fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        final int pulledToY = oldY + deltaY;
8471fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        if (pulledToY < 0) {
848506695b4cf6b22d70d2e3647d94551e721ee0430Aurimas Liutikas                            EdgeEffectCompat.onPull(mEdgeGlowTop, (float) deltaY / getHeight(),
8491b3e9466b4c4d72f28bb4448672ef8bab19b6f3eKirill Grouchnikov                                    ev.getX(activePointerIndex) / getWidth());
8501fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                            if (!mEdgeGlowBottom.isFinished()) {
8511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                                mEdgeGlowBottom.onRelease();
8521fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                            }
8531fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        } else if (pulledToY > range) {
854506695b4cf6b22d70d2e3647d94551e721ee0430Aurimas Liutikas                            EdgeEffectCompat.onPull(mEdgeGlowBottom, (float) deltaY / getHeight(),
8551b3e9466b4c4d72f28bb4448672ef8bab19b6f3eKirill Grouchnikov                                    1.f - ev.getX(activePointerIndex)
8561fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                                            / getWidth());
8571fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                            if (!mEdgeGlowTop.isFinished()) {
8581fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                                mEdgeGlowTop.onRelease();
8591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                            }
8601fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        }
8611fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        if (mEdgeGlowTop != null
8621fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                                && (!mEdgeGlowTop.isFinished() || !mEdgeGlowBottom.isFinished())) {
8631fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                            ViewCompat.postInvalidateOnAnimation(this);
8641fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        }
8651fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    }
8661fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                }
8671fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                break;
8681fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            case MotionEvent.ACTION_UP:
86976daed103193a1756535d1f59b165e98e1d17445Chris Banes                final VelocityTracker velocityTracker = mVelocityTracker;
87076daed103193a1756535d1f59b165e98e1d17445Chris Banes                velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
87176daed103193a1756535d1f59b165e98e1d17445Chris Banes                int initialVelocity = (int) velocityTracker.getYVelocity(mActivePointerId);
87276daed103193a1756535d1f59b165e98e1d17445Chris Banes                if ((Math.abs(initialVelocity) > mMinimumVelocity)) {
87376daed103193a1756535d1f59b165e98e1d17445Chris Banes                    flingWithNestedDispatch(-initialVelocity);
87476daed103193a1756535d1f59b165e98e1d17445Chris Banes                } else if (mScroller.springBack(getScrollX(), getScrollY(), 0, 0, 0,
87576daed103193a1756535d1f59b165e98e1d17445Chris Banes                        getScrollRange())) {
87676daed103193a1756535d1f59b165e98e1d17445Chris Banes                    ViewCompat.postInvalidateOnAnimation(this);
8771fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                }
878ba3bd1923ffd1ef5597deb0a9df8c1444fbf4433Chris Banes                mActivePointerId = INVALID_POINTER;
879ba3bd1923ffd1ef5597deb0a9df8c1444fbf4433Chris Banes                endDrag();
8801fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                break;
8811fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            case MotionEvent.ACTION_CANCEL:
8821fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                if (mIsBeingDragged && getChildCount() > 0) {
88300db92e217c3bc08acd09143cac8e3d3b0d0e813Chris Banes                    if (mScroller.springBack(getScrollX(), getScrollY(), 0, 0, 0,
88400db92e217c3bc08acd09143cac8e3d3b0d0e813Chris Banes                            getScrollRange())) {
88500db92e217c3bc08acd09143cac8e3d3b0d0e813Chris Banes                        ViewCompat.postInvalidateOnAnimation(this);
88600db92e217c3bc08acd09143cac8e3d3b0d0e813Chris Banes                    }
8871fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                }
888ba3bd1923ffd1ef5597deb0a9df8c1444fbf4433Chris Banes                mActivePointerId = INVALID_POINTER;
889ba3bd1923ffd1ef5597deb0a9df8c1444fbf4433Chris Banes                endDrag();
8901fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                break;
8916ed40c1f86bcb172a1f0f069cde1c571a7781aeeAurimas Liutikas            case MotionEvent.ACTION_POINTER_DOWN: {
8926ed40c1f86bcb172a1f0f069cde1c571a7781aeeAurimas Liutikas                final int index = ev.getActionIndex();
8931b3e9466b4c4d72f28bb4448672ef8bab19b6f3eKirill Grouchnikov                mLastMotionY = (int) ev.getY(index);
8941b3e9466b4c4d72f28bb4448672ef8bab19b6f3eKirill Grouchnikov                mActivePointerId = ev.getPointerId(index);
8951fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                break;
8961fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
8976ed40c1f86bcb172a1f0f069cde1c571a7781aeeAurimas Liutikas            case MotionEvent.ACTION_POINTER_UP:
8981fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                onSecondaryPointerUp(ev);
8991b3e9466b4c4d72f28bb4448672ef8bab19b6f3eKirill Grouchnikov                mLastMotionY = (int) ev.getY(ev.findPointerIndex(mActivePointerId));
9001fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                break;
9011fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
9021fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
9031fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (mVelocityTracker != null) {
9041fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            mVelocityTracker.addMovement(vtev);
9051fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
9061fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        vtev.recycle();
9071fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return true;
9081fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
9091fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
9101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private void onSecondaryPointerUp(MotionEvent ev) {
9116ed40c1f86bcb172a1f0f069cde1c571a7781aeeAurimas Liutikas        final int pointerIndex = ev.getActionIndex();
9121b3e9466b4c4d72f28bb4448672ef8bab19b6f3eKirill Grouchnikov        final int pointerId = ev.getPointerId(pointerIndex);
9131fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (pointerId == mActivePointerId) {
9141fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            // This was our active pointer going up. Choose a new
9151fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            // active pointer and adjust accordingly.
9161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            // TODO: Make this decision more intelligent.
9171fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
9181b3e9466b4c4d72f28bb4448672ef8bab19b6f3eKirill Grouchnikov            mLastMotionY = (int) ev.getY(newPointerIndex);
9191b3e9466b4c4d72f28bb4448672ef8bab19b6f3eKirill Grouchnikov            mActivePointerId = ev.getPointerId(newPointerIndex);
9201fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            if (mVelocityTracker != null) {
9211fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                mVelocityTracker.clear();
9221fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
9231fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
9241fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
9251fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
926e2104f4b5c8e3ad63570306a25e61502dfe4c418Aurimas Liutikas    @Override
9271fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public boolean onGenericMotionEvent(MotionEvent event) {
9281b3e9466b4c4d72f28bb4448672ef8bab19b6f3eKirill Grouchnikov        if ((event.getSource() & InputDeviceCompat.SOURCE_CLASS_POINTER) != 0) {
9291fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            switch (event.getAction()) {
9306ed40c1f86bcb172a1f0f069cde1c571a7781aeeAurimas Liutikas                case MotionEvent.ACTION_SCROLL: {
9311fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    if (!mIsBeingDragged) {
9326ed40c1f86bcb172a1f0f069cde1c571a7781aeeAurimas Liutikas                        final float vscroll = event.getAxisValue(MotionEvent.AXIS_VSCROLL);
9331fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        if (vscroll != 0) {
9341fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                            final int delta = (int) (vscroll * getVerticalScrollFactorCompat());
9351fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                            final int range = getScrollRange();
9361fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                            int oldScrollY = getScrollY();
9371fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                            int newScrollY = oldScrollY - delta;
9381fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                            if (newScrollY < 0) {
9391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                                newScrollY = 0;
9401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                            } else if (newScrollY > range) {
9411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                                newScrollY = range;
9421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                            }
9431fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                            if (newScrollY != oldScrollY) {
9441fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                                super.scrollTo(getScrollX(), newScrollY);
9451fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                                return true;
9461fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                            }
9471fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        }
9481fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    }
9491fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                }
9501fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
9511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
9521fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return false;
9531fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
9541fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
9551fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private float getVerticalScrollFactorCompat() {
9561fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (mVerticalScrollFactor == 0) {
9571fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            TypedValue outValue = new TypedValue();
9581fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            final Context context = getContext();
9591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            if (!context.getTheme().resolveAttribute(
9601fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    android.R.attr.listPreferredItemHeight, outValue, true)) {
9611fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                throw new IllegalStateException(
9621fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        "Expected theme to define listPreferredItemHeight.");
9631fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
9641fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            mVerticalScrollFactor = outValue.getDimension(
9651fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    context.getResources().getDisplayMetrics());
9661fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
9671fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return mVerticalScrollFactor;
9681fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
9691fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
97015375aa6fd54b036f97f99229aefab2822c8a1c9Aurimas Liutikas    @Override
9711fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    protected void onOverScrolled(int scrollX, int scrollY,
9721fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            boolean clampedX, boolean clampedY) {
9731fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        super.scrollTo(scrollX, scrollY);
9741fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
9751fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
9761fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    boolean overScrollByCompat(int deltaX, int deltaY,
9771fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            int scrollX, int scrollY,
9781fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            int scrollRangeX, int scrollRangeY,
9791fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            int maxOverScrollX, int maxOverScrollY,
9801fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            boolean isTouchEvent) {
981e12dfa03641ad9cf0ddf272675bbe7d1198adbfdAurimas Liutikas        final int overScrollMode = getOverScrollMode();
9821fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        final boolean canScrollHorizontal =
9831fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                computeHorizontalScrollRange() > computeHorizontalScrollExtent();
9841fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        final boolean canScrollVertical =
9851fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                computeVerticalScrollRange() > computeVerticalScrollExtent();
986e12dfa03641ad9cf0ddf272675bbe7d1198adbfdAurimas Liutikas        final boolean overScrollHorizontal = overScrollMode == View.OVER_SCROLL_ALWAYS
987e12dfa03641ad9cf0ddf272675bbe7d1198adbfdAurimas Liutikas                || (overScrollMode == View.OVER_SCROLL_IF_CONTENT_SCROLLS && canScrollHorizontal);
988e12dfa03641ad9cf0ddf272675bbe7d1198adbfdAurimas Liutikas        final boolean overScrollVertical = overScrollMode == View.OVER_SCROLL_ALWAYS
989e12dfa03641ad9cf0ddf272675bbe7d1198adbfdAurimas Liutikas                || (overScrollMode == View.OVER_SCROLL_IF_CONTENT_SCROLLS && canScrollVertical);
9901fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
9911fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        int newScrollX = scrollX + deltaX;
9921fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (!overScrollHorizontal) {
9931fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            maxOverScrollX = 0;
9941fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
9951fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
9961fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        int newScrollY = scrollY + deltaY;
9971fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (!overScrollVertical) {
9981fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            maxOverScrollY = 0;
9991fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
10001fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
10011fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        // Clamp values if at the limits and record
10021fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        final int left = -maxOverScrollX;
10031fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        final int right = maxOverScrollX + scrollRangeX;
10041fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        final int top = -maxOverScrollY;
10051fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        final int bottom = maxOverScrollY + scrollRangeY;
10061fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
10071fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        boolean clampedX = false;
10081fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (newScrollX > right) {
10091fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            newScrollX = right;
10101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            clampedX = true;
10111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        } else if (newScrollX < left) {
10121fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            newScrollX = left;
10131fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            clampedX = true;
10141fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
10151fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
10161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        boolean clampedY = false;
10171fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (newScrollY > bottom) {
10181fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            newScrollY = bottom;
10191fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            clampedY = true;
10201fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        } else if (newScrollY < top) {
10211fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            newScrollY = top;
10221fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            clampedY = true;
10231fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
10241fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
102576daed103193a1756535d1f59b165e98e1d17445Chris Banes        if (clampedY && !hasNestedScrollingParent(ViewCompat.TYPE_NON_TOUCH)) {
102600db92e217c3bc08acd09143cac8e3d3b0d0e813Chris Banes            mScroller.springBack(newScrollX, newScrollY, 0, 0, 0, getScrollRange());
102700db92e217c3bc08acd09143cac8e3d3b0d0e813Chris Banes        }
102800db92e217c3bc08acd09143cac8e3d3b0d0e813Chris Banes
10291fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        onOverScrolled(newScrollX, newScrollY, clampedX, clampedY);
10301fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
10311fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return clampedX || clampedY;
10321fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
10331fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
1034540222c3675801eaa141ace1c164c4d3499b4721Aurimas Liutikas    int getScrollRange() {
10351fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        int scrollRange = 0;
10361fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (getChildCount() > 0) {
10371fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            View child = getChildAt(0);
10381fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            scrollRange = Math.max(0,
10391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    child.getHeight() - (getHeight() - getPaddingBottom() - getPaddingTop()));
10401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
10411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return scrollRange;
10421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
10431fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
10441fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
10451fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * <p>
10461fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * Finds the next focusable component that fits in the specified bounds.
10471fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * </p>
10481fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *
10491fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @param topFocus look for a candidate is the one at the top of the bounds
10501fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *                 if topFocus is true, or at the bottom of the bounds if topFocus is
10511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *                 false
10521fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @param top      the top offset of the bounds in which a focusable must be
10531fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *                 found
10541fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @param bottom   the bottom offset of the bounds in which a focusable must
10551fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *                 be found
10561fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @return the next focusable component in the bounds or null if none can
10571fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *         be found
10581fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
10591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private View findFocusableViewInBounds(boolean topFocus, int top, int bottom) {
10601fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
10611fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        List<View> focusables = getFocusables(View.FOCUS_FORWARD);
10621fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        View focusCandidate = null;
10631fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
10641fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        /*
10651fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell         * A fully contained focusable is one where its top is below the bound's
10661fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell         * top, and its bottom is above the bound's bottom. A partially
10671fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell         * contained focusable is one where some part of it is within the
10681fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell         * bounds, but it also has some part that is not within bounds.  A fully contained
10691fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell         * focusable is preferred to a partially contained focusable.
10701fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell         */
10711fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        boolean foundFullyContainedFocusable = false;
10721fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
10731fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        int count = focusables.size();
10741fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        for (int i = 0; i < count; i++) {
10751fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            View view = focusables.get(i);
10761fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            int viewTop = view.getTop();
10771fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            int viewBottom = view.getBottom();
10781fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
10791fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            if (top < viewBottom && viewTop < bottom) {
10801fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                /*
10811fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                 * the focusable is in the target area, it is a candidate for
10821fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                 * focusing
10831fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                 */
10841fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
10854d2c7b7c4f194034c5f17c4bee7320d808aabe4cAurimas Liutikas                final boolean viewIsFullyContained = (top < viewTop) && (viewBottom < bottom);
10861fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
10871fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                if (focusCandidate == null) {
10881fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    /* No candidate, take this one */
10891fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    focusCandidate = view;
10901fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    foundFullyContainedFocusable = viewIsFullyContained;
10911fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                } else {
10921fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    final boolean viewIsCloserToBoundary =
10934d2c7b7c4f194034c5f17c4bee7320d808aabe4cAurimas Liutikas                            (topFocus && viewTop < focusCandidate.getTop())
10944d2c7b7c4f194034c5f17c4bee7320d808aabe4cAurimas Liutikas                                    || (!topFocus && viewBottom > focusCandidate.getBottom());
10951fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
10961fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    if (foundFullyContainedFocusable) {
10971fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        if (viewIsFullyContained && viewIsCloserToBoundary) {
10981fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                            /*
10991fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                             * We're dealing with only fully contained views, so
11001fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                             * it has to be closer to the boundary to beat our
11011fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                             * candidate
11021fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                             */
11031fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                            focusCandidate = view;
11041fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        }
11051fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    } else {
11061fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        if (viewIsFullyContained) {
11071fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                            /* Any fully contained view beats a partially contained view */
11081fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                            focusCandidate = view;
11091fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                            foundFullyContainedFocusable = true;
11101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        } else if (viewIsCloserToBoundary) {
11111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                            /*
11121fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                             * Partially contained view beats another partially
11131fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                             * contained view if it's closer
11141fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                             */
11151fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                            focusCandidate = view;
11161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        }
11171fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    }
11181fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                }
11191fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
11201fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
11211fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
11221fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return focusCandidate;
11231fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
11241fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
11251fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
11261fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * <p>Handles scrolling in response to a "page up/down" shortcut press. This
11271fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * method will scroll the view by one page up or down and give the focus
11281fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * to the topmost/bottommost component in the new visible area. If no
11291fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * component is a good candidate for focus, this scrollview reclaims the
11301fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * focus.</p>
11311fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *
11321fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @param direction the scroll direction: {@link android.view.View#FOCUS_UP}
11331fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *                  to go one page up or
11341fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *                  {@link android.view.View#FOCUS_DOWN} to go one page down
11351fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @return true if the key event is consumed by this method, false otherwise
11361fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
11371fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public boolean pageScroll(int direction) {
11381fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        boolean down = direction == View.FOCUS_DOWN;
11391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        int height = getHeight();
11401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
11411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (down) {
11421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            mTempRect.top = getScrollY() + height;
11431fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            int count = getChildCount();
11441fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            if (count > 0) {
11451fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                View view = getChildAt(count - 1);
11461fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                if (mTempRect.top + height > view.getBottom()) {
11471fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    mTempRect.top = view.getBottom() - height;
11481fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                }
11491fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
11501fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        } else {
11511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            mTempRect.top = getScrollY() - height;
11521fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            if (mTempRect.top < 0) {
11531fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                mTempRect.top = 0;
11541fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
11551fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
11561fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        mTempRect.bottom = mTempRect.top + height;
11571fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
11581fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return scrollAndFocus(direction, mTempRect.top, mTempRect.bottom);
11591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
11601fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
11611fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
11621fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * <p>Handles scrolling in response to a "home/end" shortcut press. This
11631fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * method will scroll the view to the top or bottom and give the focus
11641fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * to the topmost/bottommost component in the new visible area. If no
11651fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * component is a good candidate for focus, this scrollview reclaims the
11661fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * focus.</p>
11671fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *
11681fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @param direction the scroll direction: {@link android.view.View#FOCUS_UP}
11691fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *                  to go the top of the view or
11701fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *                  {@link android.view.View#FOCUS_DOWN} to go the bottom
11711fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @return true if the key event is consumed by this method, false otherwise
11721fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
11731fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public boolean fullScroll(int direction) {
11741fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        boolean down = direction == View.FOCUS_DOWN;
11751fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        int height = getHeight();
11761fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
11771fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        mTempRect.top = 0;
11781fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        mTempRect.bottom = height;
11791fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
11801fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (down) {
11811fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            int count = getChildCount();
11821fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            if (count > 0) {
11831fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                View view = getChildAt(count - 1);
11841fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                mTempRect.bottom = view.getBottom() + getPaddingBottom();
11851fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                mTempRect.top = mTempRect.bottom - height;
11861fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
11871fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
11881fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
11891fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return scrollAndFocus(direction, mTempRect.top, mTempRect.bottom);
11901fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
11911fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
11921fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
11931fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * <p>Scrolls the view to make the area defined by <code>top</code> and
11941fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * <code>bottom</code> visible. This method attempts to give the focus
11951fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * to a component visible in this area. If no component can be focused in
11961fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * the new visible area, the focus is reclaimed by this ScrollView.</p>
11971fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *
11981fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @param direction the scroll direction: {@link android.view.View#FOCUS_UP}
11991fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *                  to go upward, {@link android.view.View#FOCUS_DOWN} to downward
12001fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @param top       the top offset of the new area to be made visible
12011fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @param bottom    the bottom offset of the new area to be made visible
12021fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @return true if the key event is consumed by this method, false otherwise
12031fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
12041fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private boolean scrollAndFocus(int direction, int top, int bottom) {
12051fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        boolean handled = true;
12061fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
12071fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        int height = getHeight();
12081fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        int containerTop = getScrollY();
12091fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        int containerBottom = containerTop + height;
12101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        boolean up = direction == View.FOCUS_UP;
12111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
12121fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        View newFocused = findFocusableViewInBounds(up, top, bottom);
12131fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (newFocused == null) {
12141fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            newFocused = this;
12151fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
12161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
12171fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (top >= containerTop && bottom <= containerBottom) {
12181fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            handled = false;
12191fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        } else {
12201fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            int delta = up ? (top - containerTop) : (bottom - containerBottom);
12211fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            doScrollY(delta);
12221fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
12231fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
12241fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (newFocused != findFocus()) newFocused.requestFocus(direction);
12251fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
12261fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return handled;
12271fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
12281fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
12291fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
12301fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * Handle scrolling in response to an up or down arrow click.
12311fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *
12321fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @param direction The direction corresponding to the arrow key that was
12331fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *                  pressed
12341fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @return True if we consumed the event, false otherwise
12351fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
12361fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public boolean arrowScroll(int direction) {
12371fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
12381fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        View currentFocused = findFocus();
12391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (currentFocused == this) currentFocused = null;
12401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
12411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        View nextFocused = FocusFinder.getInstance().findNextFocus(this, currentFocused, direction);
12421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
12431fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        final int maxJump = getMaxScrollAmount();
12441fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
12451fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (nextFocused != null && isWithinDeltaOfScreen(nextFocused, maxJump, getHeight())) {
12461fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            nextFocused.getDrawingRect(mTempRect);
12471fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            offsetDescendantRectToMyCoords(nextFocused, mTempRect);
12481fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            int scrollDelta = computeScrollDeltaToGetChildRectOnScreen(mTempRect);
12491fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            doScrollY(scrollDelta);
12501fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            nextFocused.requestFocus(direction);
12511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        } else {
12521fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            // no new focus
12531fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            int scrollDelta = maxJump;
12541fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
12551fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            if (direction == View.FOCUS_UP && getScrollY() < scrollDelta) {
12561fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                scrollDelta = getScrollY();
12571fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            } else if (direction == View.FOCUS_DOWN) {
12581fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                if (getChildCount() > 0) {
12591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    int daBottom = getChildAt(0).getBottom();
12601fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    int screenBottom = getScrollY() + getHeight() - getPaddingBottom();
12611fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    if (daBottom - screenBottom < maxJump) {
12621fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        scrollDelta = daBottom - screenBottom;
12631fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    }
12641fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                }
12651fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
12661fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            if (scrollDelta == 0) {
12671fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                return false;
12681fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
12691fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            doScrollY(direction == View.FOCUS_DOWN ? scrollDelta : -scrollDelta);
12701fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
12711fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
12721fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (currentFocused != null && currentFocused.isFocused()
12731fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                && isOffScreen(currentFocused)) {
12741fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            // previously focused item still has focus and is off screen, give
12751fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            // it up (take it back to ourselves)
12761fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            // (also, need to temporarily force FOCUS_BEFORE_DESCENDANTS so we are
12771fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            // sure to
12781fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            // get it)
12791fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            final int descendantFocusability = getDescendantFocusability();  // save
12801fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
12811fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            requestFocus();
12821fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            setDescendantFocusability(descendantFocusability);  // restore
12831fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
12841fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return true;
12851fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
12861fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
12871fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
12881fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @return whether the descendant of this scroll view is scrolled off
12891fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *  screen.
12901fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
12911fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private boolean isOffScreen(View descendant) {
12921fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return !isWithinDeltaOfScreen(descendant, 0, getHeight());
12931fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
12941fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
12951fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
12961fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @return whether the descendant of this scroll view is within delta
12971fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *  pixels of being on the screen.
12981fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
12991fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private boolean isWithinDeltaOfScreen(View descendant, int delta, int height) {
13001fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        descendant.getDrawingRect(mTempRect);
13011fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        offsetDescendantRectToMyCoords(descendant, mTempRect);
13021fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
13031fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return (mTempRect.bottom + delta) >= getScrollY()
13041fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                && (mTempRect.top - delta) <= (getScrollY() + height);
13051fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
13061fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
13071fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
13081fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * Smooth scroll by a Y delta
13091fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *
13101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @param delta the number of pixels to scroll by on the Y axis
13111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
13121fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private void doScrollY(int delta) {
13131fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (delta != 0) {
13141fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            if (mSmoothScrollingEnabled) {
13151fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                smoothScrollBy(0, delta);
13161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            } else {
13171fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                scrollBy(0, delta);
13181fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
13191fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
13201fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
13211fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
13221fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
13231fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * Like {@link View#scrollBy}, but scroll smoothly instead of immediately.
13241fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *
13251fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @param dx the number of pixels to scroll by on the X axis
13261fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @param dy the number of pixels to scroll by on the Y axis
13271fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
13281fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public final void smoothScrollBy(int dx, int dy) {
13291fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (getChildCount() == 0) {
13301fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            // Nothing to do.
13311fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            return;
13321fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
13331fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        long duration = AnimationUtils.currentAnimationTimeMillis() - mLastScroll;
13341fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (duration > ANIMATED_SCROLL_GAP) {
13351fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            final int height = getHeight() - getPaddingBottom() - getPaddingTop();
13361fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            final int bottom = getChildAt(0).getHeight();
13371fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            final int maxY = Math.max(0, bottom - height);
13381fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            final int scrollY = getScrollY();
13391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            dy = Math.max(0, Math.min(scrollY + dy, maxY)) - scrollY;
13401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
13411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            mScroller.startScroll(getScrollX(), scrollY, 0, dy);
13421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            ViewCompat.postInvalidateOnAnimation(this);
13431fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        } else {
13441fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            if (!mScroller.isFinished()) {
13451fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                mScroller.abortAnimation();
13461fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
13471fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            scrollBy(dx, dy);
13481fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
13491fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        mLastScroll = AnimationUtils.currentAnimationTimeMillis();
13501fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
13511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
13521fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
13531fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * Like {@link #scrollTo}, but scroll smoothly instead of immediately.
13541fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *
13551fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @param x the position where to scroll on the X axis
13561fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @param y the position where to scroll on the Y axis
13571fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
13581fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public final void smoothScrollTo(int x, int y) {
13591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        smoothScrollBy(x - getScrollX(), y - getScrollY());
13601fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
13611fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
13621fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
13631fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * <p>The scroll range of a scroll view is the overall height of all of its
13641fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * children.</p>
1365c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki     * @hide
13661fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
13678e10080c914d1ad0784394fa3026b85535535847Aurimas Liutikas    @RestrictTo(LIBRARY_GROUP)
13681fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
1369c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki    public int computeVerticalScrollRange() {
13701fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        final int count = getChildCount();
13711fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        final int contentHeight = getHeight() - getPaddingBottom() - getPaddingTop();
13721fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (count == 0) {
13731fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            return contentHeight;
13741fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
13751fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
13761fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        int scrollRange = getChildAt(0).getBottom();
13771fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        final int scrollY = getScrollY();
13781fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        final int overscrollBottom = Math.max(0, scrollRange - contentHeight);
13791fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (scrollY < 0) {
13801fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            scrollRange -= scrollY;
13811fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        } else if (scrollY > overscrollBottom) {
13821fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            scrollRange += scrollY - overscrollBottom;
13831fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
13841fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
13851fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return scrollRange;
13861fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
13871fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
1388c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki    /** @hide */
13898e10080c914d1ad0784394fa3026b85535535847Aurimas Liutikas    @RestrictTo(LIBRARY_GROUP)
13901fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
1391c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki    public int computeVerticalScrollOffset() {
13921fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return Math.max(0, super.computeVerticalScrollOffset());
13931fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
13941fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
1395c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki    /** @hide */
13968e10080c914d1ad0784394fa3026b85535535847Aurimas Liutikas    @RestrictTo(LIBRARY_GROUP)
1397c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki    @Override
1398c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki    public int computeVerticalScrollExtent() {
1399c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki        return super.computeVerticalScrollExtent();
1400c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki    }
1401c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki
1402c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki    /** @hide */
14038e10080c914d1ad0784394fa3026b85535535847Aurimas Liutikas    @RestrictTo(LIBRARY_GROUP)
1404c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki    @Override
1405c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki    public int computeHorizontalScrollRange() {
1406c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki        return super.computeHorizontalScrollRange();
1407c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki    }
1408c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki
1409c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki    /** @hide */
14108e10080c914d1ad0784394fa3026b85535535847Aurimas Liutikas    @RestrictTo(LIBRARY_GROUP)
1411c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki    @Override
1412c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki    public int computeHorizontalScrollOffset() {
1413c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki        return super.computeHorizontalScrollOffset();
1414c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki    }
1415c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki
1416c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki    /** @hide */
14178e10080c914d1ad0784394fa3026b85535535847Aurimas Liutikas    @RestrictTo(LIBRARY_GROUP)
1418c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki    @Override
1419c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki    public int computeHorizontalScrollExtent() {
1420c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki        return super.computeHorizontalScrollExtent();
1421c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki    }
1422c608a7d5a7fddcf06af43d58d7c62dedd38cffc9Yuichi Araki
14231fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
14244d2c7b7c4f194034c5f17c4bee7320d808aabe4cAurimas Liutikas    protected void measureChild(View child, int parentWidthMeasureSpec,
14254d2c7b7c4f194034c5f17c4bee7320d808aabe4cAurimas Liutikas            int parentHeightMeasureSpec) {
14261fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        ViewGroup.LayoutParams lp = child.getLayoutParams();
14271fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
14281fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        int childWidthMeasureSpec;
14291fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        int childHeightMeasureSpec;
14301fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
14311fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, getPaddingLeft()
14321fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                + getPaddingRight(), lp.width);
14331fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
14341fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
14351fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
14361fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
14371fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
14381fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
14391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
14401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed,
14411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            int parentHeightMeasureSpec, int heightUsed) {
14421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
14431fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
14441fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
14451fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                getPaddingLeft() + getPaddingRight() + lp.leftMargin + lp.rightMargin
14461fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        + widthUsed, lp.width);
14471fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        final int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
14481fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                lp.topMargin + lp.bottomMargin, MeasureSpec.UNSPECIFIED);
14491fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
14501fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
14511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
14521fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
14531fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
14541fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public void computeScroll() {
14551fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (mScroller.computeScrollOffset()) {
145676daed103193a1756535d1f59b165e98e1d17445Chris Banes            final int x = mScroller.getCurrX();
145776daed103193a1756535d1f59b165e98e1d17445Chris Banes            final int y = mScroller.getCurrY();
145876daed103193a1756535d1f59b165e98e1d17445Chris Banes
145976daed103193a1756535d1f59b165e98e1d17445Chris Banes            int dy = y - mLastScrollerY;
14601fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
146176daed103193a1756535d1f59b165e98e1d17445Chris Banes            // Dispatch up to parent
146276daed103193a1756535d1f59b165e98e1d17445Chris Banes            if (dispatchNestedPreScroll(0, dy, mScrollConsumed, null, ViewCompat.TYPE_NON_TOUCH)) {
146376daed103193a1756535d1f59b165e98e1d17445Chris Banes                dy -= mScrollConsumed[1];
146476daed103193a1756535d1f59b165e98e1d17445Chris Banes            }
146576daed103193a1756535d1f59b165e98e1d17445Chris Banes
146676daed103193a1756535d1f59b165e98e1d17445Chris Banes            if (dy != 0) {
14671fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                final int range = getScrollRange();
146876daed103193a1756535d1f59b165e98e1d17445Chris Banes                final int oldScrollY = getScrollY();
146976daed103193a1756535d1f59b165e98e1d17445Chris Banes
147076daed103193a1756535d1f59b165e98e1d17445Chris Banes                overScrollByCompat(0, dy, getScrollX(), oldScrollY, 0, range, 0, 0, false);
147176daed103193a1756535d1f59b165e98e1d17445Chris Banes
147276daed103193a1756535d1f59b165e98e1d17445Chris Banes                final int scrolledDeltaY = getScrollY() - oldScrollY;
147376daed103193a1756535d1f59b165e98e1d17445Chris Banes                final int unconsumedY = dy - scrolledDeltaY;
147476daed103193a1756535d1f59b165e98e1d17445Chris Banes
147576daed103193a1756535d1f59b165e98e1d17445Chris Banes                if (!dispatchNestedScroll(0, scrolledDeltaY, 0, unconsumedY, null,
147676daed103193a1756535d1f59b165e98e1d17445Chris Banes                        ViewCompat.TYPE_NON_TOUCH)) {
147776daed103193a1756535d1f59b165e98e1d17445Chris Banes                    final int mode = getOverScrollMode();
147876daed103193a1756535d1f59b165e98e1d17445Chris Banes                    final boolean canOverscroll = mode == OVER_SCROLL_ALWAYS
147976daed103193a1756535d1f59b165e98e1d17445Chris Banes                            || (mode == OVER_SCROLL_IF_CONTENT_SCROLLS && range > 0);
148076daed103193a1756535d1f59b165e98e1d17445Chris Banes                    if (canOverscroll) {
148176daed103193a1756535d1f59b165e98e1d17445Chris Banes                        ensureGlows();
148276daed103193a1756535d1f59b165e98e1d17445Chris Banes                        if (y <= 0 && oldScrollY > 0) {
148376daed103193a1756535d1f59b165e98e1d17445Chris Banes                            mEdgeGlowTop.onAbsorb((int) mScroller.getCurrVelocity());
148476daed103193a1756535d1f59b165e98e1d17445Chris Banes                        } else if (y >= range && oldScrollY < range) {
148576daed103193a1756535d1f59b165e98e1d17445Chris Banes                            mEdgeGlowBottom.onAbsorb((int) mScroller.getCurrVelocity());
148676daed103193a1756535d1f59b165e98e1d17445Chris Banes                        }
14871fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    }
14881fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                }
14891fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
149076daed103193a1756535d1f59b165e98e1d17445Chris Banes
149176daed103193a1756535d1f59b165e98e1d17445Chris Banes            // Finally update the scroll positions and post an invalidation
149276daed103193a1756535d1f59b165e98e1d17445Chris Banes            mLastScrollerY = y;
149376daed103193a1756535d1f59b165e98e1d17445Chris Banes            ViewCompat.postInvalidateOnAnimation(this);
149476daed103193a1756535d1f59b165e98e1d17445Chris Banes        } else {
149576daed103193a1756535d1f59b165e98e1d17445Chris Banes            // We can't scroll any more, so stop any indirect scrolling
149676daed103193a1756535d1f59b165e98e1d17445Chris Banes            if (hasNestedScrollingParent(ViewCompat.TYPE_NON_TOUCH)) {
149776daed103193a1756535d1f59b165e98e1d17445Chris Banes                stopNestedScroll(ViewCompat.TYPE_NON_TOUCH);
149876daed103193a1756535d1f59b165e98e1d17445Chris Banes            }
149976daed103193a1756535d1f59b165e98e1d17445Chris Banes            // and reset the scroller y
150076daed103193a1756535d1f59b165e98e1d17445Chris Banes            mLastScrollerY = 0;
15011fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
15021fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
15031fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
15041fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
15051fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * Scrolls the view to the given child.
15061fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *
15071fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @param child the View to scroll to
15081fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
15091fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private void scrollToChild(View child) {
15101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        child.getDrawingRect(mTempRect);
15111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
15121fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        /* Offset from child's local coordinates to ScrollView coordinates */
15131fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        offsetDescendantRectToMyCoords(child, mTempRect);
15141fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
15151fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        int scrollDelta = computeScrollDeltaToGetChildRectOnScreen(mTempRect);
15161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
15171fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (scrollDelta != 0) {
15181fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            scrollBy(0, scrollDelta);
15191fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
15201fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
15211fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
15221fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
15231fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * If rect is off screen, scroll just enough to get it (or at least the
15241fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * first screen size chunk of it) on screen.
15251fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *
15261fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @param rect      The rectangle.
15271fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @param immediate True to scroll immediately without animation
15281fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @return true if scrolling was performed
15291fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
15301fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private boolean scrollToChildRect(Rect rect, boolean immediate) {
15311fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        final int delta = computeScrollDeltaToGetChildRectOnScreen(rect);
15321fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        final boolean scroll = delta != 0;
15331fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (scroll) {
15341fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            if (immediate) {
15351fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                scrollBy(0, delta);
15361fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            } else {
15371fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                smoothScrollBy(0, delta);
15381fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
15391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
15401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return scroll;
15411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
15421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
15431fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
15441fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * Compute the amount to scroll in the Y direction in order to get
15451fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * a rectangle completely on the screen (or, if taller than the screen,
15461fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * at least the first screen size chunk of it).
15471fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *
15481fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @param rect The rect.
15491fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @return The scroll delta.
15501fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
15511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    protected int computeScrollDeltaToGetChildRectOnScreen(Rect rect) {
15521fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (getChildCount() == 0) return 0;
15531fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
15541fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        int height = getHeight();
15551fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        int screenTop = getScrollY();
15561fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        int screenBottom = screenTop + height;
15571fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
15581fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        int fadingEdge = getVerticalFadingEdgeLength();
15591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
15601fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        // leave room for top fading edge as long as rect isn't at very top
15611fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (rect.top > 0) {
15621fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            screenTop += fadingEdge;
15631fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
15641fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
15651fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        // leave room for bottom fading edge as long as rect isn't at very bottom
15661fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (rect.bottom < getChildAt(0).getHeight()) {
15671fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            screenBottom -= fadingEdge;
15681fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
15691fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
15701fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        int scrollYDelta = 0;
15711fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
15721fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (rect.bottom > screenBottom && rect.top > screenTop) {
15731fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            // need to move down to get it in view: move down just enough so
15741fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            // that the entire rectangle is in view (or at least the first
15751fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            // screen size chunk).
15761fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
15771fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            if (rect.height() > height) {
15781fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                // just enough to get screen size chunk on
15791fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                scrollYDelta += (rect.top - screenTop);
15801fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            } else {
15811fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                // get entire rect at bottom of screen
15821fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                scrollYDelta += (rect.bottom - screenBottom);
15831fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
15841fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
15851fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            // make sure we aren't scrolling beyond the end of our content
15861fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            int bottom = getChildAt(0).getBottom();
15871fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            int distanceToBottom = bottom - screenBottom;
15881fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            scrollYDelta = Math.min(scrollYDelta, distanceToBottom);
15891fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
15901fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        } else if (rect.top < screenTop && rect.bottom < screenBottom) {
15911fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            // need to move up to get it in view: move up just enough so that
15921fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            // entire rectangle is in view (or at least the first screen
15931fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            // size chunk of it).
15941fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
15951fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            if (rect.height() > height) {
15961fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                // screen size chunk
15971fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                scrollYDelta -= (screenBottom - rect.bottom);
15981fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            } else {
15991fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                // entire rect at top
16001fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                scrollYDelta -= (screenTop - rect.top);
16011fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
16021fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
16031fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            // make sure we aren't scrolling any further than the top our content
16041fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            scrollYDelta = Math.max(scrollYDelta, -getScrollY());
16051fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
16061fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return scrollYDelta;
16071fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
16081fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
16091fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
16101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public void requestChildFocus(View child, View focused) {
16111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (!mIsLayoutDirty) {
16121fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            scrollToChild(focused);
16131fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        } else {
16141fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            // The child may not be laid out yet, we can't compute the scroll yet
16151fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            mChildToScrollTo = focused;
16161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
16171fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        super.requestChildFocus(child, focused);
16181fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
16191fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
16201fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
16211fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
16221fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * When looking for focus in children of a scroll view, need to be a little
16231fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * more careful not to give focus to something that is scrolled off screen.
16241fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *
16251fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * This is more expensive than the default {@link android.view.ViewGroup}
16261fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * implementation, otherwise this behavior might have been made the default.
16271fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
16281fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
16291fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    protected boolean onRequestFocusInDescendants(int direction,
16301fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            Rect previouslyFocusedRect) {
16311fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
16321fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        // convert from forward / backward notation to up / down / left / right
16331fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        // (ugh).
16341fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (direction == View.FOCUS_FORWARD) {
16351fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            direction = View.FOCUS_DOWN;
16361fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        } else if (direction == View.FOCUS_BACKWARD) {
16371fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            direction = View.FOCUS_UP;
16381fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
16391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
16404d2c7b7c4f194034c5f17c4bee7320d808aabe4cAurimas Liutikas        final View nextFocus = previouslyFocusedRect == null
16414d2c7b7c4f194034c5f17c4bee7320d808aabe4cAurimas Liutikas                ? FocusFinder.getInstance().findNextFocus(this, null, direction)
16424d2c7b7c4f194034c5f17c4bee7320d808aabe4cAurimas Liutikas                : FocusFinder.getInstance().findNextFocusFromRect(
16434d2c7b7c4f194034c5f17c4bee7320d808aabe4cAurimas Liutikas                        this, previouslyFocusedRect, direction);
16441fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
16451fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (nextFocus == null) {
16461fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            return false;
16471fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
16481fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
16491fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (isOffScreen(nextFocus)) {
16501fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            return false;
16511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
16521fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
16531fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return nextFocus.requestFocus(direction, previouslyFocusedRect);
16541fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
16551fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
16561fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
16571fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public boolean requestChildRectangleOnScreen(View child, Rect rectangle,
16581fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            boolean immediate) {
16591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        // offset into coordinate space of this scroll view
16601fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        rectangle.offset(child.getLeft() - child.getScrollX(),
16611fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                child.getTop() - child.getScrollY());
16621fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
16631fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return scrollToChildRect(rectangle, immediate);
16641fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
16651fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
16661fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
16671fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public void requestLayout() {
16681fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        mIsLayoutDirty = true;
16691fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        super.requestLayout();
16701fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
16711fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
16721fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
16731fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    protected void onLayout(boolean changed, int l, int t, int r, int b) {
16741fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        super.onLayout(changed, l, t, r, b);
16751fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        mIsLayoutDirty = false;
16761fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        // Give a child focus if it needs it
16771fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (mChildToScrollTo != null && isViewDescendantOf(mChildToScrollTo, this)) {
16781fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            scrollToChild(mChildToScrollTo);
16791fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
16801fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        mChildToScrollTo = null;
16811fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
16821fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (!mIsLaidOut) {
16831fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            if (mSavedState != null) {
16841fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                scrollTo(getScrollX(), mSavedState.scrollPosition);
16851fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                mSavedState = null;
16861fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            } // mScrollY default value is "0"
16871fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
16881fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            final int childHeight = (getChildCount() > 0) ? getChildAt(0).getMeasuredHeight() : 0;
16891fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            final int scrollRange = Math.max(0,
16901fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    childHeight - (b - t - getPaddingBottom() - getPaddingTop()));
16911fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
16921fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            // Don't forget to clamp
16931fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            if (getScrollY() > scrollRange) {
16941fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                scrollTo(getScrollX(), scrollRange);
16951fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            } else if (getScrollY() < 0) {
16961fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                scrollTo(getScrollX(), 0);
16971fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
16981fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
16991fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
17001fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        // Calling this with the present values causes it to re-claim them
17011fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        scrollTo(getScrollX(), getScrollY());
17021fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        mIsLaidOut = true;
17031fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
17041fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
17051fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
17061fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public void onAttachedToWindow() {
1707d344e81ad956ec445038f5ba2ca2ebd1831b1f8aAlan Viverette        super.onAttachedToWindow();
1708d344e81ad956ec445038f5ba2ca2ebd1831b1f8aAlan Viverette
17091fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        mIsLaidOut = false;
17101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
17111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
17121fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
17131fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
17141fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        super.onSizeChanged(w, h, oldw, oldh);
17151fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
17161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        View currentFocused = findFocus();
17174d2c7b7c4f194034c5f17c4bee7320d808aabe4cAurimas Liutikas        if (null == currentFocused || this == currentFocused) {
17181fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            return;
17194d2c7b7c4f194034c5f17c4bee7320d808aabe4cAurimas Liutikas        }
17201fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
17211fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        // If the currently-focused view was visible on the screen when the
17221fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        // screen was at the old height, then scroll the screen to make that
17231fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        // view visible with the new screen height.
17241fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (isWithinDeltaOfScreen(currentFocused, 0, oldh)) {
17251fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            currentFocused.getDrawingRect(mTempRect);
17261fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            offsetDescendantRectToMyCoords(currentFocused, mTempRect);
17271fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            int scrollDelta = computeScrollDeltaToGetChildRectOnScreen(mTempRect);
17281fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            doScrollY(scrollDelta);
17291fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
17301fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
17311fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
17321fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
17331fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * Return true if child is a descendant of parent, (or equal to the parent).
17341fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
17351fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private static boolean isViewDescendantOf(View child, View parent) {
17361fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (child == parent) {
17371fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            return true;
17381fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
17391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
17401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        final ViewParent theParent = child.getParent();
17411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return (theParent instanceof ViewGroup) && isViewDescendantOf((View) theParent, parent);
17421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
17431fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
17441fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
17451fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * Fling the scroll view
17461fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *
17471fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * @param velocityY The initial velocity in the Y direction. Positive
17481fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *                  numbers mean that the finger/cursor is moving down the screen,
17491fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *                  which means we want to scroll towards the top.
17501fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
17511fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public void fling(int velocityY) {
17521fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (getChildCount() > 0) {
175376daed103193a1756535d1f59b165e98e1d17445Chris Banes            startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL, ViewCompat.TYPE_NON_TOUCH);
175476daed103193a1756535d1f59b165e98e1d17445Chris Banes            mScroller.fling(getScrollX(), getScrollY(), // start
175576daed103193a1756535d1f59b165e98e1d17445Chris Banes                    0, velocityY, // velocities
175676daed103193a1756535d1f59b165e98e1d17445Chris Banes                    0, 0, // x
175776daed103193a1756535d1f59b165e98e1d17445Chris Banes                    Integer.MIN_VALUE, Integer.MAX_VALUE, // y
175876daed103193a1756535d1f59b165e98e1d17445Chris Banes                    0, 0); // overscroll
175976daed103193a1756535d1f59b165e98e1d17445Chris Banes            mLastScrollerY = getScrollY();
17601fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            ViewCompat.postInvalidateOnAnimation(this);
17611fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
17621fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
17631fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
17641fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private void flingWithNestedDispatch(int velocityY) {
17651fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        final int scrollY = getScrollY();
17664d2c7b7c4f194034c5f17c4bee7320d808aabe4cAurimas Liutikas        final boolean canFling = (scrollY > 0 || velocityY > 0)
17674d2c7b7c4f194034c5f17c4bee7320d808aabe4cAurimas Liutikas                && (scrollY < getScrollRange() || velocityY < 0);
17681fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (!dispatchNestedPreFling(0, velocityY)) {
17691fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            dispatchNestedFling(0, velocityY, canFling);
177076daed103193a1756535d1f59b165e98e1d17445Chris Banes            fling(velocityY);
17711fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
17721fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
17731fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
17741fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private void endDrag() {
17751fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        mIsBeingDragged = false;
17761fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
17771fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        recycleVelocityTracker();
177876daed103193a1756535d1f59b165e98e1d17445Chris Banes        stopNestedScroll(ViewCompat.TYPE_TOUCH);
17791fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
17801fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (mEdgeGlowTop != null) {
17811fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            mEdgeGlowTop.onRelease();
17821fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            mEdgeGlowBottom.onRelease();
17831fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
17841fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
17851fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
17861fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    /**
17871fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * {@inheritDoc}
17881fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     *
17891fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     * <p>This version also clamps the scrolling to the bounds of our child.
17901fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell     */
17911fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
17921fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public void scrollTo(int x, int y) {
17931fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        // we rely on the fact the View.scrollBy calls scrollTo.
17941fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (getChildCount() > 0) {
17951fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            View child = getChildAt(0);
17961fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            x = clamp(x, getWidth() - getPaddingRight() - getPaddingLeft(), child.getWidth());
17971fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            y = clamp(y, getHeight() - getPaddingBottom() - getPaddingTop(), child.getHeight());
17981fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            if (x != getScrollX() || y != getScrollY()) {
17991fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                super.scrollTo(x, y);
18001fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
18011fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
18021fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
18031fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
18041fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private void ensureGlows() {
1805e12dfa03641ad9cf0ddf272675bbe7d1198adbfdAurimas Liutikas        if (getOverScrollMode() != View.OVER_SCROLL_NEVER) {
18061fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            if (mEdgeGlowTop == null) {
18071fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                Context context = getContext();
1808506695b4cf6b22d70d2e3647d94551e721ee0430Aurimas Liutikas                mEdgeGlowTop = new EdgeEffect(context);
1809506695b4cf6b22d70d2e3647d94551e721ee0430Aurimas Liutikas                mEdgeGlowBottom = new EdgeEffect(context);
18101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
18111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        } else {
18121fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            mEdgeGlowTop = null;
18131fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            mEdgeGlowBottom = null;
18141fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
18151fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
18161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
18171fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
18181fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    public void draw(Canvas canvas) {
18191fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        super.draw(canvas);
18201fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (mEdgeGlowTop != null) {
18211fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            final int scrollY = getScrollY();
18221fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            if (!mEdgeGlowTop.isFinished()) {
18231fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                final int restoreCount = canvas.save();
1824bcb5323316d898cc22873efc2a145a5912e8a75cshepshapard                int width = getWidth();
1825bcb5323316d898cc22873efc2a145a5912e8a75cshepshapard                int height = getHeight();
1826bcb5323316d898cc22873efc2a145a5912e8a75cshepshapard                int xTranslation = 0;
1827bcb5323316d898cc22873efc2a145a5912e8a75cshepshapard                int yTranslation = Math.min(0, scrollY);
1828bcb5323316d898cc22873efc2a145a5912e8a75cshepshapard                if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP || getClipToPadding()) {
1829bcb5323316d898cc22873efc2a145a5912e8a75cshepshapard                    width -= getPaddingLeft() + getPaddingRight();
1830bcb5323316d898cc22873efc2a145a5912e8a75cshepshapard                    xTranslation += getPaddingLeft();
1831bcb5323316d898cc22873efc2a145a5912e8a75cshepshapard                }
1832bcb5323316d898cc22873efc2a145a5912e8a75cshepshapard                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && getClipToPadding()) {
1833bcb5323316d898cc22873efc2a145a5912e8a75cshepshapard                    height -= getPaddingTop() + getPaddingBottom();
1834bcb5323316d898cc22873efc2a145a5912e8a75cshepshapard                    yTranslation += getPaddingTop();
1835bcb5323316d898cc22873efc2a145a5912e8a75cshepshapard                }
1836bcb5323316d898cc22873efc2a145a5912e8a75cshepshapard                canvas.translate(xTranslation, yTranslation);
1837bcb5323316d898cc22873efc2a145a5912e8a75cshepshapard                mEdgeGlowTop.setSize(width, height);
18381fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                if (mEdgeGlowTop.draw(canvas)) {
18391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    ViewCompat.postInvalidateOnAnimation(this);
18401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                }
18411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                canvas.restoreToCount(restoreCount);
18421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
18431fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            if (!mEdgeGlowBottom.isFinished()) {
18441fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                final int restoreCount = canvas.save();
1845bcb5323316d898cc22873efc2a145a5912e8a75cshepshapard                int width = getWidth();
1846bcb5323316d898cc22873efc2a145a5912e8a75cshepshapard                int height = getHeight();
1847bcb5323316d898cc22873efc2a145a5912e8a75cshepshapard                int xTranslation = 0;
1848bcb5323316d898cc22873efc2a145a5912e8a75cshepshapard                int yTranslation = Math.max(getScrollRange(), scrollY) + height;
1849bcb5323316d898cc22873efc2a145a5912e8a75cshepshapard                if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP || getClipToPadding()) {
1850bcb5323316d898cc22873efc2a145a5912e8a75cshepshapard                    width -= getPaddingLeft() + getPaddingRight();
1851bcb5323316d898cc22873efc2a145a5912e8a75cshepshapard                    xTranslation += getPaddingLeft();
1852bcb5323316d898cc22873efc2a145a5912e8a75cshepshapard                }
1853bcb5323316d898cc22873efc2a145a5912e8a75cshepshapard                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && getClipToPadding()) {
1854bcb5323316d898cc22873efc2a145a5912e8a75cshepshapard                    height -= getPaddingTop() + getPaddingBottom();
1855bcb5323316d898cc22873efc2a145a5912e8a75cshepshapard                    yTranslation -= getPaddingBottom();
1856bcb5323316d898cc22873efc2a145a5912e8a75cshepshapard                }
1857bcb5323316d898cc22873efc2a145a5912e8a75cshepshapard                canvas.translate(xTranslation - width, yTranslation);
18581fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                canvas.rotate(180, width, 0);
18591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                mEdgeGlowBottom.setSize(width, height);
18601fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                if (mEdgeGlowBottom.draw(canvas)) {
18611fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    ViewCompat.postInvalidateOnAnimation(this);
18621fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                }
18631fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                canvas.restoreToCount(restoreCount);
18641fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
18651fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
18661fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
18671fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
18681fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    private static int clamp(int n, int my, int child) {
18691fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        if (my >= child || n < 0) {
18701fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            /* my >= child is this case:
18711fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell             *                    |--------------- me ---------------|
18721fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell             *     |------ child ------|
18731fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell             * or
18741fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell             *     |--------------- me ---------------|
18751fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell             *            |------ child ------|
18761fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell             * or
18771fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell             *     |--------------- me ---------------|
18781fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell             *                                  |------ child ------|
18791fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell             *
18801fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell             * n < 0 is this case:
18811fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell             *     |------ me ------|
18821fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell             *                    |-------- child --------|
18831fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell             *     |-- mScrollX --|
18841fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell             */
18851fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            return 0;
18861fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
18874d2c7b7c4f194034c5f17c4bee7320d808aabe4cAurimas Liutikas        if ((my + n) > child) {
18881fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            /* this case:
18891fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell             *                    |------ me ------|
18901fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell             *     |------ child ------|
18911fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell             *     |-- mScrollX --|
18921fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell             */
18934d2c7b7c4f194034c5f17c4bee7320d808aabe4cAurimas Liutikas            return child - my;
18941fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
18951fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return n;
18961fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
18971fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
18981fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
18991fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    protected void onRestoreInstanceState(Parcelable state) {
1900bb58a82daf11bf3c056d1cd5887aa26435d37b69Aurimas Liutikas        if (!(state instanceof SavedState)) {
1901bb58a82daf11bf3c056d1cd5887aa26435d37b69Aurimas Liutikas            super.onRestoreInstanceState(state);
1902bb58a82daf11bf3c056d1cd5887aa26435d37b69Aurimas Liutikas            return;
1903bb58a82daf11bf3c056d1cd5887aa26435d37b69Aurimas Liutikas        }
1904bb58a82daf11bf3c056d1cd5887aa26435d37b69Aurimas Liutikas
19051fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        SavedState ss = (SavedState) state;
19061fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        super.onRestoreInstanceState(ss.getSuperState());
19071fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        mSavedState = ss;
19081fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        requestLayout();
19091fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
19101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
19111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    @Override
19121fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    protected Parcelable onSaveInstanceState() {
19131fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        Parcelable superState = super.onSaveInstanceState();
19141fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        SavedState ss = new SavedState(superState);
19151fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        ss.scrollPosition = getScrollY();
19161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        return ss;
19171fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
19181fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
19191fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    static class SavedState extends BaseSavedState {
19201fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        public int scrollPosition;
19211fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
19221fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        SavedState(Parcelable superState) {
19231fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            super(superState);
19241fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
19251fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
19264d2c7b7c4f194034c5f17c4bee7320d808aabe4cAurimas Liutikas        SavedState(Parcel source) {
19271fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            super(source);
19281fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            scrollPosition = source.readInt();
19291fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
19301fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
19311fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        @Override
19321fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        public void writeToParcel(Parcel dest, int flags) {
19331fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            super.writeToParcel(dest, flags);
19341fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            dest.writeInt(scrollPosition);
19351fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
19361fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
19371fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        @Override
19381fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        public String toString() {
19391fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            return "HorizontalScrollView.SavedState{"
19401fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    + Integer.toHexString(System.identityHashCode(this))
19411fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    + " scrollPosition=" + scrollPosition + "}";
19421fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
19431fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
19444d2c7b7c4f194034c5f17c4bee7320d808aabe4cAurimas Liutikas        public static final Parcelable.Creator<SavedState> CREATOR =
19454d2c7b7c4f194034c5f17c4bee7320d808aabe4cAurimas Liutikas                new Parcelable.Creator<SavedState>() {
194615375aa6fd54b036f97f99229aefab2822c8a1c9Aurimas Liutikas            @Override
19471fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            public SavedState createFromParcel(Parcel in) {
19481fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                return new SavedState(in);
19491fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
19501fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
195115375aa6fd54b036f97f99229aefab2822c8a1c9Aurimas Liutikas            @Override
19521fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            public SavedState[] newArray(int size) {
19531fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                return new SavedState[size];
19541fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
19551fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        };
19561fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
19571fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
19581fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    static class AccessibilityDelegate extends AccessibilityDelegateCompat {
19591fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        @Override
19601fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        public boolean performAccessibilityAction(View host, int action, Bundle arguments) {
19611fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            if (super.performAccessibilityAction(host, action, arguments)) {
19621fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                return true;
19631fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
19641fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            final NestedScrollView nsvHost = (NestedScrollView) host;
19651fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            if (!nsvHost.isEnabled()) {
19661fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                return false;
19671fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
19681fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            switch (action) {
19691fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                case AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD: {
19701fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    final int viewportHeight = nsvHost.getHeight() - nsvHost.getPaddingBottom()
19711fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                            - nsvHost.getPaddingTop();
19721fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    final int targetScrollY = Math.min(nsvHost.getScrollY() + viewportHeight,
19731fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                            nsvHost.getScrollRange());
19741fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    if (targetScrollY != nsvHost.getScrollY()) {
19751fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        nsvHost.smoothScrollTo(0, targetScrollY);
19761fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        return true;
19771fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    }
19781fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                }
19791fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                return false;
19801fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                case AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD: {
19811fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    final int viewportHeight = nsvHost.getHeight() - nsvHost.getPaddingBottom()
19821fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                            - nsvHost.getPaddingTop();
19831fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    final int targetScrollY = Math.max(nsvHost.getScrollY() - viewportHeight, 0);
19841fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    if (targetScrollY != nsvHost.getScrollY()) {
19851fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        nsvHost.smoothScrollTo(0, targetScrollY);
19861fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        return true;
19871fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    }
19881fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                }
19891fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                return false;
19901fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
19911fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            return false;
19921fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
19931fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
19941fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        @Override
19951fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) {
19961fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            super.onInitializeAccessibilityNodeInfo(host, info);
19971fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            final NestedScrollView nsvHost = (NestedScrollView) host;
19981fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            info.setClassName(ScrollView.class.getName());
19991fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            if (nsvHost.isEnabled()) {
20001fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                final int scrollRange = nsvHost.getScrollRange();
20011fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                if (scrollRange > 0) {
20021fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    info.setScrollable(true);
20031fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    if (nsvHost.getScrollY() > 0) {
20041fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD);
20051fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    }
20061fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    if (nsvHost.getScrollY() < scrollRange) {
20071fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                        info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD);
20081fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                    }
20091fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell                }
20101fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            }
20111fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
20121fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell
20131fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        @Override
20141fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) {
20151fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            super.onInitializeAccessibilityEvent(host, event);
20161fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            final NestedScrollView nsvHost = (NestedScrollView) host;
20171fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            event.setClassName(ScrollView.class.getName());
20181fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell            final boolean scrollable = nsvHost.getScrollRange() > 0;
201914d02ef06479168249fdfeea47bc105d05e88749Aurimas Liutikas            event.setScrollable(scrollable);
202014d02ef06479168249fdfeea47bc105d05e88749Aurimas Liutikas            event.setScrollX(nsvHost.getScrollX());
202114d02ef06479168249fdfeea47bc105d05e88749Aurimas Liutikas            event.setScrollY(nsvHost.getScrollY());
202214d02ef06479168249fdfeea47bc105d05e88749Aurimas Liutikas            AccessibilityRecordCompat.setMaxScrollX(event, nsvHost.getScrollX());
202314d02ef06479168249fdfeea47bc105d05e88749Aurimas Liutikas            AccessibilityRecordCompat.setMaxScrollY(event, nsvHost.getScrollRange());
20241fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell        }
20251fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell    }
20261fcce4485ef99aca928ebfb877859c5ecd47716cAdam Powell}
2027