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