Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0b8bb428 authored by Adam Powell's avatar Adam Powell
Browse files

Overscrolling modifications. Overscroll will not allow the user to

scroll content out of view. Scrolling will slow down halfway to the
barrier point. API added in View. AbsListView, ScrollView,
HorizontalScrollView all use this API. Overscrolling uses haptic
feedback. Added scroll barrier pattern to config.xml.
parent 8bd3f749
Loading
Loading
Loading
Loading
+57 −0
Original line number Diff line number Diff line
@@ -164726,6 +164726,17 @@
 visibility="public"
>
</field>
<field name="SCROLL_BARRIER"
 type="int"
 transient="false"
 volatile="false"
 value="2"
 static="true"
 final="true"
 deprecated="not deprecated"
 visibility="public"
>
</field>
<field name="VIRTUAL_KEY"
 type="int"
 transient="false"
@@ -173140,6 +173151,25 @@
<parameter name="heightMeasureSpec" type="int">
</parameter>
</method>
<method name="onOverscrolled"
 return="void"
 abstract="false"
 native="false"
 synchronized="false"
 static="false"
 final="false"
 deprecated="not deprecated"
 visibility="protected"
>
<parameter name="scrollX" type="int">
</parameter>
<parameter name="scrollY" type="int">
</parameter>
<parameter name="clampedX" type="boolean">
</parameter>
<parameter name="clampedY" type="boolean">
</parameter>
</method>
<method name="onRestoreInstanceState"
 return="void"
 abstract="false"
@@ -173293,6 +173323,33 @@
<parameter name="visibility" type="int">
</parameter>
</method>
<method name="overscrollBy"
 return="boolean"
 abstract="false"
 native="false"
 synchronized="false"
 static="false"
 final="false"
 deprecated="not deprecated"
 visibility="protected"
>
<parameter name="deltaX" type="int">
</parameter>
<parameter name="deltaY" type="int">
</parameter>
<parameter name="scrollX" type="int">
</parameter>
<parameter name="scrollY" type="int">
</parameter>
<parameter name="scrollRangeX" type="int">
</parameter>
<parameter name="scrollRangeY" type="int">
</parameter>
<parameter name="maxOverscrollX" type="int">
</parameter>
<parameter name="maxOverscrollY" type="int">
</parameter>
</method>
<method name="performClick"
 return="boolean"
 abstract="false"
+5 −0
Original line number Diff line number Diff line
@@ -35,6 +35,11 @@ public class HapticFeedbackConstants {
     */
    public static final int VIRTUAL_KEY = 1;
    
    /**
     * The user has hit the barrier point while scrolling a view.
     */
    public static final int SCROLL_BARRIER = 2;
    
    /**
     * This is a private constant.  Feel free to renumber as desired.
     * @hide
+112 −0
Original line number Diff line number Diff line
@@ -8548,6 +8548,118 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
        return factory.inflate(resource, root);
    }
    
    /**
     * Scroll the view with standard behavior for scrolling beyond the normal
     * content boundaries. Views that call this method should override
     * {@link #onOverscrolled()} to respond to the results of an overscroll
     * operation.
     * 
     * Views can use this method to handle any touch or fling-based scrolling.
     * 
     * @param deltaX Change in X in pixels 
     * @param deltaY Change in Y in pixels
     * @param scrollX Current X scroll value in pixels before applying deltaX
     * @param scrollY Current Y scroll value in pixels before applying deltaY
     * @param scrollRangeX Maximum content scroll range along the X axis
     * @param scrollRangeY Maximum content scroll range along the Y axis
     * @param maxOverscrollX Number of pixels to overscroll by in either direction
     *          along the X axis.
     * @param maxOverscrollY Number of pixels to overscroll by in either direction
     *          along the Y axis.
     * @return true if scrolling was clamped to an overscroll boundary along either
     *          axis, false otherwise.
     */
    protected boolean overscrollBy(int deltaX, int deltaY,
            int scrollX, int scrollY,
            int scrollRangeX, int scrollRangeY,
            int maxOverscrollX, int maxOverscrollY) {
        // Scale the scroll amount if we're in the dropoff zone
        final int dropoffX = maxOverscrollX / 2;
        final int dropoffLeft = -dropoffX;
        final int dropoffRight = dropoffX + scrollRangeX;
        int newScrollX;
        if ((scrollX < dropoffLeft && deltaX < 0) ||
                (scrollX > dropoffRight && deltaX > 0)) {
            newScrollX = scrollX + deltaX / 2;
        } else {
            newScrollX = scrollX + deltaX;
            if (newScrollX > dropoffRight && deltaX > 0) {
                int extra = newScrollX - dropoffRight;
                newScrollX = dropoffRight + extra / 2;
            } else if (newScrollX < dropoffLeft && deltaX < 0) {
                int extra = newScrollX - dropoffLeft;
                newScrollX = dropoffLeft + extra / 2;
            }
        }
        
        final int dropoffY = maxOverscrollY / 2;
        final int dropoffTop = -dropoffY;
        final int dropoffBottom = dropoffY + scrollRangeY;
        int newScrollY;
        if ((scrollY < dropoffTop && deltaY < 0) ||
                (scrollY > dropoffBottom && deltaY > 0)) {
            newScrollY = scrollY + deltaY / 2;
        } else {
            newScrollY = scrollY + deltaY;
            if (newScrollY > dropoffBottom && deltaY > 0) {
                int extra = newScrollY - dropoffBottom;
                newScrollY = dropoffBottom + extra / 2;
            } else if (newScrollY < dropoffTop && deltaY < 0) {
                int extra = newScrollY - dropoffTop;
                newScrollY = dropoffTop + extra / 2;
            }
        }

        // Clamp values if at the limits and record
        final int left = -maxOverscrollX;
        final int right = maxOverscrollX + scrollRangeX;
        final int top = -maxOverscrollY;
        final int bottom = maxOverscrollY + scrollRangeY;

        boolean clampedX = false;
        if (newScrollX > right) {
            newScrollX = right;
            clampedX = true;
        } else if (newScrollX < left) {
            newScrollX = left;
            clampedX = true;
        }
        
        boolean clampedY = false;
        if (newScrollY > bottom) {
            newScrollY = bottom;
            clampedY = true;
        } else if (newScrollY < top) {
            newScrollY = top;
            clampedY = true;
        }
        
        // Bump the device with some haptic feedback if we're at the edge
        // and didn't start there.
        if ((clampedX && scrollX != left && scrollX != right) ||
                (clampedY && scrollY != top && scrollY != bottom)) {
            performHapticFeedback(HapticFeedbackConstants.SCROLL_BARRIER);
        }

        onOverscrolled(newScrollX, newScrollY, clampedX, clampedY);
        
        return clampedX || clampedY;
    }
    
    /**
     * Called by {@link #overscrollBy(int, int, int, int, int, int, int, int)} to
     * respond to the results of an overscroll operation.
     * 
     * @param scrollX New X scroll value in pixels
     * @param scrollY New Y scroll value in pixels
     * @param clampedX True if scrollX was clamped to an overscroll boundary
     * @param clampedY True if scrollY was clamped to an overscroll boundary
     */
    protected void onOverscrolled(int scrollX, int scrollY,
            boolean clampedX, boolean clampedY) {
        // Intentionally empty.
    }
    
    /**
     * A MeasureSpec encapsulates the layout requirements passed from parent to child.
     * Each MeasureSpec represents a requirement for either the width or the height.
+81 −20
Original line number Diff line number Diff line
@@ -378,6 +378,16 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te

    private ContextMenuInfo mContextMenuInfo = null;
    
    /**
     * Maximum distance to overscroll by
     */
    private int mOverscrollMax;
    
    /**
     * Content height divided by this is the overscroll limit.
     */
    private static final int OVERSCROLL_LIMIT_DIVISOR = 3;

    /**
     * Used to request a layout when we changed touch mode
     */
@@ -1048,7 +1058,8 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
                final int top = view.getTop();
                int height = view.getHeight();
                if (height > 0) {
                    return Math.max(firstPosition * 100 - (top * 100) / height, 0);
                    return Math.max(firstPosition * 100 - (top * 100) / height +
                            (int)((float)mScrollY / getHeight() * mItemCount * 100), 0);
                }
            } else {
                int index;
@@ -1068,7 +1079,17 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te

    @Override
    protected int computeVerticalScrollRange() {
        return mSmoothScrollbarEnabled ? Math.max(mItemCount * 100, 0) : mItemCount;
        int result;
        if (mSmoothScrollbarEnabled) {
            result = Math.max(mItemCount * 100, 0);
            if (mScrollY != 0) {
                // Compensate for overscroll
                result += Math.abs((int) ((float) mScrollY / getHeight() * mItemCount * 100));
            }
        } else {
            result = mItemCount;
        }
        return result;
    }

    @Override
@@ -1129,6 +1150,8 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
        mInLayout = true;
        layoutChildren();
        mInLayout = false;
        
        mOverscrollMax = (b - t) / OVERSCROLL_LIMIT_DIVISOR;
    }

    /**
@@ -2078,11 +2101,13 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
                        // Check if the top of the motion view is where it is
                        // supposed to be
                        final int motionViewRealTop = motionView.getTop();
                        final int motionViewNewTop = mMotionViewNewTop;
                        if (atEdge) {
                            // Apply overscroll
                            
                            mScrollY -= incrementalDeltaY - (motionViewRealTop - motionViewPrevTop);
                            int overscroll = -incrementalDeltaY - 
                                    (motionViewRealTop - motionViewPrevTop);
                            overscrollBy(0, overscroll, 0, mScrollY, 0, 0,
                                    0, getOverscrollMax());
                            mTouchMode = TOUCH_MODE_OVERSCROLL;
                            invalidate();
                        }
@@ -2126,7 +2151,8 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
                            mMotionPosition = motionPosition;
                        }
                    } else {
                        mScrollY -= incrementalDeltaY;
                        overscrollBy(0, -incrementalDeltaY, 0, mScrollY, 0, 0,
                                0, getOverscrollMax());
                        invalidate();
                    }
                    mLastY = y;
@@ -2312,6 +2338,28 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
        return true;
    }
    
    @Override
    protected void onOverscrolled(int scrollX, int scrollY,
            boolean clampedX, boolean clampedY) {
        mScrollY = scrollY;
        if (clampedY) {
            // Velocity is broken by hitting the limit; don't start a fling off of this.
            if (mVelocityTracker != null) {
                mVelocityTracker.clear();
            }
        }
    }
    
    private int getOverscrollMax() {
        final int childCount = getChildCount();
        if (childCount > 0) {
            return Math.min(mOverscrollMax,
                    getChildAt(childCount - 1).getBottom() / OVERSCROLL_LIMIT_DIVISOR);
        } else {
            return mOverscrollMax;
        }
    }

    @Override
    public void draw(Canvas canvas) {
        super.draw(canvas);
@@ -2532,15 +2580,17 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
                    delta = Math.max(-(getHeight() - mPaddingBottom - mPaddingTop - 1), delta);
                }

                // Do something different on overscroll - offsetChildrenTopAndBottom()
                trackMotionScroll(delta, delta);

                // Check to see if we have bumped into the scroll limit
                View motionView = getChildAt(mMotionPosition - mFirstPosition);
                int oldTop = 0;
                if (motionView != null) {
                    // Check if the top of the motion view is where it is
                    // supposed to be
                    if (motionView.getTop() != mMotionViewNewTop) {
                    oldTop = motionView.getTop();
                }
                if (trackMotionScroll(delta, delta)) {
                    if (motionView != null) {
                        // Tweak the scroll for how far we overshot
                        mScrollY -= delta - (motionView.getTop() - oldTop);
                    }
                    float vel = scroller.getCurrVelocity();
                    if (delta > 0) {
                        vel = -vel;
@@ -2548,7 +2598,6 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
                    startOverfling(Math.round(vel));
                    break;
                }
                }

                if (more) {
                    invalidate();
@@ -2570,9 +2619,14 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
            case TOUCH_MODE_OVERFLING: {
                final OverScroller scroller = mScroller;
                if (scroller.computeScrollOffset()) {
                    mScrollY = scroller.getCurrY();
                    final int scrollY = mScrollY;
                    final int deltaY = scroller.getCurrY() - scrollY;
                    if (overscrollBy(0, deltaY, 0, scrollY, 0, 0, 0, getOverscrollMax())) {
                        startSpringback();
                    } else {
                        invalidate();
                        post(this);
                    }
                } else {
                    endFling();
                }
@@ -2703,6 +2757,10 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
                final int lastViewIndex = getChildCount() - 1;
                final int lastPos = firstPos + lastViewIndex;
                
                if (lastViewIndex < 0) {
                    return;
                }

                if (lastPos == mLastSeenPos) {
                    // No new views, let things keep going.
                    post(this);
@@ -2764,6 +2822,9 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
                }

                final View firstView = getChildAt(0);
                if (firstView == null) {
                    return;
                }
                final int firstViewTop = firstView.getTop();

                smoothScrollBy(firstViewTop - mExtraScroll, mScrollDuration);
+60 −9
Original line number Diff line number Diff line
@@ -461,7 +461,8 @@ public class HorizontalScrollView extends FrameLayout {
                final int deltaX = (int) (mLastMotionX - x);
                mLastMotionX = x;

                super.scrollTo(mScrollX + deltaX, mScrollY);
                overscrollBy(deltaX, 0, mScrollX, 0, getScrollRange(), 0,
                        getOverscrollMax(), 0);
                break;
            case MotionEvent.ACTION_UP:
                final VelocityTracker velocityTracker = mVelocityTracker;
@@ -472,8 +473,7 @@ public class HorizontalScrollView extends FrameLayout {
                    if ((Math.abs(initialVelocity) > mMinimumVelocity)) {
                        fling(-initialVelocity);
                    } else {
                        final int right = Math.max(0, getChildAt(0).getHeight() - 
                                (getHeight() - mPaddingRight - mPaddingLeft));
                        final int right = getScrollRange();
                        if (mScroller.springback(mScrollX, mScrollY, 0, 0, right, 0)) {
                            invalidate();
                        }
@@ -488,6 +488,41 @@ public class HorizontalScrollView extends FrameLayout {
        return true;
    }
    
    @Override
    protected void onOverscrolled(int scrollX, int scrollY,
            boolean clampedX, boolean clampedY) {
        // Treat animating scrolls differently; see #computeScroll() for why.
        if (!mScroller.isFinished()) {
            mScrollX = scrollX;
            mScrollY = scrollY;
            if (clampedX) {
                mScroller.springback(mScrollX, mScrollY, 0, getScrollRange(), 0, 0);
            }
        } else {
            super.scrollTo(scrollX, scrollY);
        }
    }
    
    private int getOverscrollMax() {
        int childCount = getChildCount();
        int containerOverscroll = (getWidth() - mPaddingLeft - mPaddingRight) / 3;
        if (childCount > 0) {
            return Math.min(containerOverscroll, getChildAt(0).getWidth() / 3);
        } else {
            return containerOverscroll;
        }
    }
    
    private int getScrollRange() {
        int scrollRange = 0;
        if (getChildCount() > 0) {
            View child = getChildAt(0);
            scrollRange = Math.max(0,
                    child.getWidth() - getWidth() - mPaddingLeft - mPaddingRight);
        }
        return scrollRange;
    }

    /**
     * <p>
     * Finds the next focusable component that fits in this View's bounds
@@ -856,9 +891,26 @@ public class HorizontalScrollView extends FrameLayout {
    @Override
    protected int computeHorizontalScrollRange() {
        int count = getChildCount();
        return count == 0 ? getWidth() : getChildAt(0).getRight();
        if (count == 0) {
            return getWidth();
        }
        
        int scrollRange = getChildAt(0).getRight();
        int scrollX = mScrollX;
        int overscrollRight = scrollRange - getWidth() - mPaddingLeft - mPaddingRight;
        if (scrollX < 0) {
            scrollRange -= scrollX;
        } else if (scrollX > overscrollRight) {
            scrollRange += scrollX - overscrollRight;
        }
        
        return scrollRange;
    }
    
    @Override
    protected int computeHorizontalScrollOffset() {
        return Math.max(0, super.computeHorizontalScrollOffset());
    }

    @Override
    protected void measureChild(View child, int parentWidthMeasureSpec, int parentHeightMeasureSpec) {
@@ -913,10 +965,9 @@ public class HorizontalScrollView extends FrameLayout {
            int x = mScroller.getCurrX();
            int y = mScroller.getCurrY();

            mScrollX = x;
            mScrollY = y;

            if (oldX != mScrollX || oldY != mScrollY) {
            if (oldX != x || oldY != y) {
                overscrollBy(x - oldX, y - oldY, oldX, oldY, getScrollRange(), 0,
                        getOverscrollMax(), 0);
                onScrollChanged(mScrollX, mScrollY, oldX, oldY);
            }

Loading