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

Commit 828bff7d authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Support fling back in scrolling containers"

parents fc9dcc5f 28b3b51c
Loading
Loading
Loading
Loading
+85 −3
Original line number Diff line number Diff line
@@ -241,6 +241,14 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
     */
    public static final int CHOICE_MODE_MULTIPLE_MODAL = 3;

    /**
     * When flinging the stretch towards scrolling content, it should destretch quicker than the
     * fling would normally do. The visual effect of flinging the stretch looks strange as little
     * appears to happen at first and then when the stretch disappears, the content starts
     * scrolling quickly.
     */
    private static final float FLING_DESTRETCH_FACTOR = 4f;

    /**
     * The thread that created this view.
     */
@@ -4216,9 +4224,23 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
                        // fling further.
                        boolean flingVelocity = Math.abs(initialVelocity) > mMinimumVelocity;
                        if (flingVelocity && !mEdgeGlowTop.isFinished()) {
                            if (shouldAbsorb(mEdgeGlowTop, initialVelocity)) {
                                mEdgeGlowTop.onAbsorb(initialVelocity);
                            } else {
                                if (mFlingRunnable == null) {
                                    mFlingRunnable = new FlingRunnable();
                                }
                                mFlingRunnable.start(-initialVelocity);
                            }
                        } else if (flingVelocity && !mEdgeGlowBottom.isFinished()) {
                            if (shouldAbsorb(mEdgeGlowBottom, -initialVelocity)) {
                                mEdgeGlowBottom.onAbsorb(-initialVelocity);
                            } else {
                                if (mFlingRunnable == null) {
                                    mFlingRunnable = new FlingRunnable();
                                }
                                mFlingRunnable.start(-initialVelocity);
                            }
                        } else if (flingVelocity
                                && !((mFirstPosition == 0
                                && firstChildTop == contentTop - mOverscrollDistance)
@@ -4301,6 +4323,60 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
        }
    }

    /**
     * Returns true if edgeEffect should call onAbsorb() with veclocity or false if it should
     * animate with a fling. It will animate with a fling if the velocity will remove the
     * EdgeEffect through its normal operation.
     *
     * @param edgeEffect The EdgeEffect that might absorb the velocity.
     * @param velocity The velocity of the fling motion
     * @return true if the velocity should be absorbed or false if it should be flung.
     */
    private boolean shouldAbsorb(EdgeEffect edgeEffect, int velocity) {
        if (velocity > 0) {
            return true;
        }
        float distance = edgeEffect.getDistance() * getHeight();

        // This is flinging without the spring, so let's see if it will fling past the overscroll
        if (mFlingRunnable == null) {
            mFlingRunnable = new FlingRunnable();
        }
        float flingDistance = mFlingRunnable.getSplineFlingDistance(-velocity);

        return flingDistance < distance;
    }

    /**
     * Used by consumeFlingInHorizontalStretch() and consumeFlinInVerticalStretch() for
     * consuming deltas from EdgeEffects
     * @param unconsumed The unconsumed delta that the EdgeEffets may consume
     * @return The unconsumed delta after the EdgeEffects have had an opportunity to consume.
     */
    private int consumeFlingInStretch(int unconsumed) {
        if (unconsumed < 0 && mEdgeGlowTop != null && mEdgeGlowTop.getDistance() != 0f) {
            int size = getHeight();
            float deltaDistance = unconsumed * FLING_DESTRETCH_FACTOR / size;
            int consumed = Math.round(size / FLING_DESTRETCH_FACTOR
                    * mEdgeGlowTop.onPullDistance(deltaDistance, 0.5f));
            if (consumed != unconsumed) {
                mEdgeGlowTop.finish();
            }
            return unconsumed - consumed;
        }
        if (unconsumed > 0 && mEdgeGlowBottom != null && mEdgeGlowBottom.getDistance() != 0f) {
            int size = getHeight();
            float deltaDistance = -unconsumed * FLING_DESTRETCH_FACTOR / size;
            int consumed = Math.round(-size / FLING_DESTRETCH_FACTOR
                    * mEdgeGlowBottom.onPullDistance(deltaDistance, 0.5f));
            if (consumed != unconsumed) {
                mEdgeGlowBottom.finish();
            }
            return unconsumed - consumed;
        }
        return unconsumed;
    }

    private boolean shouldDisplayEdgeEffects() {
        return getOverScrollMode() != OVER_SCROLL_NEVER;
    }
@@ -4783,6 +4859,10 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
            mScroller = new OverScroller(getContext());
        }

        float getSplineFlingDistance(int velocity) {
            return (float) mScroller.getSplineFlingDistance(velocity);
        }

        // Use AbsListView#fling(int) instead
        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
        void start(int initialVelocity) {
@@ -4905,6 +4985,8 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
                }

                if (mItemCount == 0 || getChildCount() == 0) {
                    mEdgeGlowBottom.onRelease();
                    mEdgeGlowTop.onRelease();
                    endFling();
                    return;
                }
@@ -4915,7 +4997,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te

                // Flip sign to convert finger direction to list items direction
                // (e.g. finger moving down means list is moving towards the top)
                int delta = mLastFlingY - y;
                int delta = consumeFlingInStretch(mLastFlingY - y);

                // Pretend that each frame of a fling scroll is a touch scroll
                if (delta > 0) {
+77 −5
Original line number Diff line number Diff line
@@ -77,6 +77,14 @@ public class HorizontalScrollView extends FrameLayout {

    private static final String TAG = "HorizontalScrollView";

    /**
     * When flinging the stretch towards scrolling content, it should destretch quicker than the
     * fling would normally do. The visual effect of flinging the stretch looks strange as little
     * appears to happen at first and then when the stretch disappears, the content starts
     * scrolling quickly.
     */
    private static final float FLING_DESTRETCH_FACTOR = 4f;

    private long mLastScroll;

    private final Rect mTempRect = new Rect();
@@ -1456,18 +1464,19 @@ public class HorizontalScrollView extends FrameLayout {
            int oldY = mScrollY;
            int x = mScroller.getCurrX();
            int y = mScroller.getCurrY();
            int deltaX = consumeFlingInStretch(x - oldX);

            if (oldX != x || oldY != y) {
            if (deltaX != 0 || oldY != y) {
                final int range = getScrollRange();
                final int overscrollMode = getOverScrollMode();
                final boolean canOverscroll = overscrollMode == OVER_SCROLL_ALWAYS ||
                        (overscrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS && range > 0);

                overScrollBy(x - oldX, y - oldY, oldX, oldY, range, 0,
                overScrollBy(deltaX, y - oldY, oldX, oldY, range, 0,
                        mOverflingDistance, 0, false);
                onScrollChanged(mScrollX, mScrollY, oldX, oldY);

                if (canOverscroll) {
                if (canOverscroll && deltaX != 0) {
                    if (x < 0 && oldX >= 0) {
                        mEdgeGlowLeft.onAbsorb((int) mScroller.getCurrVelocity());
                    } else if (x > range && oldX <= range) {
@@ -1482,6 +1491,36 @@ public class HorizontalScrollView extends FrameLayout {
        }
    }

    /**
     * Used by consumeFlingInHorizontalStretch() and consumeFlinInVerticalStretch() for
     * consuming deltas from EdgeEffects
     * @param unconsumed The unconsumed delta that the EdgeEffets may consume
     * @return The unconsumed delta after the EdgeEffects have had an opportunity to consume.
     */
    private int consumeFlingInStretch(int unconsumed) {
        if (unconsumed > 0 && mEdgeGlowLeft != null && mEdgeGlowLeft.getDistance() != 0f) {
            int size = getWidth();
            float deltaDistance = -unconsumed * FLING_DESTRETCH_FACTOR / size;
            int consumed = Math.round(-size / FLING_DESTRETCH_FACTOR
                    * mEdgeGlowLeft.onPullDistance(deltaDistance, 0.5f));
            if (consumed != unconsumed) {
                mEdgeGlowLeft.finish();
            }
            return unconsumed - consumed;
        }
        if (unconsumed < 0 && mEdgeGlowRight != null && mEdgeGlowRight.getDistance() != 0f) {
            int size = getWidth();
            float deltaDistance = unconsumed * FLING_DESTRETCH_FACTOR / size;
            int consumed = Math.round(size / FLING_DESTRETCH_FACTOR
                    * mEdgeGlowRight.onPullDistance(deltaDistance, 0.5f));
            if (consumed != unconsumed) {
                mEdgeGlowRight.finish();
            }
            return unconsumed - consumed;
        }
        return unconsumed;
    }

    /**
     * Scrolls the view to the given child.
     *
@@ -1746,11 +1785,23 @@ public class HorizontalScrollView extends FrameLayout {

            int maxScroll = Math.max(0, right - width);

            boolean shouldFling = false;
            if (mScrollX == 0 && !mEdgeGlowLeft.isFinished()) {
                if (shouldAbsorb(mEdgeGlowLeft, -velocityX)) {
                    mEdgeGlowLeft.onAbsorb(-velocityX);
                } else {
                    shouldFling = true;
                }
            } else if (mScrollX == maxScroll && !mEdgeGlowRight.isFinished()) {
                if (shouldAbsorb(mEdgeGlowRight, velocityX)) {
                    mEdgeGlowRight.onAbsorb(velocityX);
                } else {
                    shouldFling = true;
                }
            } else {
                shouldFling = true;
            }
            if (shouldFling) {
                mScroller.fling(mScrollX, mScrollY, velocityX, 0, 0,
                        maxScroll, 0, 0, width / 2, 0);

@@ -1773,6 +1824,27 @@ public class HorizontalScrollView extends FrameLayout {
        }
    }

    /**
     * Returns true if edgeEffect should call onAbsorb() with veclocity or false if it should
     * animate with a fling. It will animate with a fling if the velocity will remove the
     * EdgeEffect through its normal operation.
     *
     * @param edgeEffect The EdgeEffect that might absorb the velocity.
     * @param velocity The velocity of the fling motion
     * @return true if the velocity should be absorbed or false if it should be flung.
     */
    private boolean shouldAbsorb(EdgeEffect edgeEffect, int velocity) {
        if (velocity > 0) {
            return true;
        }
        float distance = edgeEffect.getDistance() * getWidth();

        // This is flinging without the spring, so let's see if it will fling past the overscroll
        float flingDistance = (float) mScroller.getSplineFlingDistance(-velocity);

        return flingDistance < distance;
    }

    /**
     * {@inheritDoc}
     *
+4 −0
Original line number Diff line number Diff line
@@ -527,6 +527,10 @@ public class OverScroller {
                Math.signum(yvel) == Math.signum(dy);
    }

    double getSplineFlingDistance(int velocity) {
        return mScrollerY.getSplineFlingDistance(velocity);
    }

    static class SplineOverScroller {
        // Initial position
        private int mStart;
+73 −5
Original line number Diff line number Diff line
@@ -85,6 +85,14 @@ public class ScrollView extends FrameLayout {

    private static final String TAG = "ScrollView";

    /**
     * When flinging the stretch towards scrolling content, it should destretch quicker than the
     * fling would normally do. The visual effect of flinging the stretch looks strange as little
     * appears to happen at first and then when the stretch disappears, the content starts
     * scrolling quickly.
     */
    private static final float FLING_DESTRETCH_FACTOR = 4f;

    @UnsupportedAppUsage
    private long mLastScroll;

@@ -1488,18 +1496,19 @@ public class ScrollView extends FrameLayout {
            int oldY = mScrollY;
            int x = mScroller.getCurrX();
            int y = mScroller.getCurrY();
            int deltaY = consumeFlingInStretch(y - oldY);

            if (oldX != x || oldY != y) {
            if (oldX != x || deltaY != 0) {
                final int range = getScrollRange();
                final int overscrollMode = getOverScrollMode();
                final boolean canOverscroll = overscrollMode == OVER_SCROLL_ALWAYS ||
                        (overscrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS && range > 0);

                overScrollBy(x - oldX, y - oldY, oldX, oldY, 0, range,
                overScrollBy(x - oldX, deltaY, oldX, oldY, 0, range,
                        0, mOverflingDistance, false);
                onScrollChanged(mScrollX, mScrollY, oldX, oldY);

                if (canOverscroll) {
                if (canOverscroll && deltaY != 0) {
                    if (y < 0 && oldY >= 0) {
                        mEdgeGlowTop.onAbsorb((int) mScroller.getCurrVelocity());
                    } else if (y > range && oldY <= range) {
@@ -1520,6 +1529,36 @@ public class ScrollView extends FrameLayout {
        }
    }

    /**
     * Used by consumeFlingInHorizontalStretch() and consumeFlinInVerticalStretch() for
     * consuming deltas from EdgeEffects
     * @param unconsumed The unconsumed delta that the EdgeEffets may consume
     * @return The unconsumed delta after the EdgeEffects have had an opportunity to consume.
     */
    private int consumeFlingInStretch(int unconsumed) {
        if (unconsumed > 0 && mEdgeGlowTop != null && mEdgeGlowTop.getDistance() != 0f) {
            int size = getHeight();
            float deltaDistance = -unconsumed * FLING_DESTRETCH_FACTOR / size;
            int consumed = Math.round(-size / FLING_DESTRETCH_FACTOR
                    * mEdgeGlowTop.onPullDistance(deltaDistance, 0.5f));
            if (consumed != unconsumed) {
                mEdgeGlowTop.finish();
            }
            return unconsumed - consumed;
        }
        if (unconsumed < 0 && mEdgeGlowBottom != null && mEdgeGlowBottom.getDistance() != 0f) {
            int size = getHeight();
            float deltaDistance = unconsumed * FLING_DESTRETCH_FACTOR / size;
            int consumed = Math.round(size / FLING_DESTRETCH_FACTOR
                    * mEdgeGlowBottom.onPullDistance(deltaDistance, 0.5f));
            if (consumed != unconsumed) {
                mEdgeGlowBottom.finish();
            }
            return unconsumed - consumed;
        }
        return unconsumed;
    }

    /**
     * Scrolls the view to the given child.
     *
@@ -1803,14 +1842,43 @@ public class ScrollView extends FrameLayout {
                fling(velocityY);
            } else if (!consumed) {
                if (!mEdgeGlowTop.isFinished()) {
                    if (shouldAbsorb(mEdgeGlowTop, -velocityY)) {
                        mEdgeGlowTop.onAbsorb(-velocityY);
                    } else {
                        fling(velocityY);
                    }
                } else if (!mEdgeGlowBottom.isFinished()) {
                    if (shouldAbsorb(mEdgeGlowBottom, velocityY)) {
                        mEdgeGlowBottom.onAbsorb(velocityY);
                    } else {
                        fling(velocityY);
                    }
                }
            }
        }
    }

    /**
     * Returns true if edgeEffect should call onAbsorb() with veclocity or false if it should
     * animate with a fling. It will animate with a fling if the velocity will remove the
     * EdgeEffect through its normal operation.
     *
     * @param edgeEffect The EdgeEffect that might absorb the velocity.
     * @param velocity The velocity of the fling motion
     * @return true if the velocity should be absorbed or false if it should be flung.
     */
    private boolean shouldAbsorb(EdgeEffect edgeEffect, int velocity) {
        if (velocity > 0) {
            return true;
        }
        float distance = edgeEffect.getDistance() * getHeight();

        // This is flinging without the spring, so let's see if it will fling past the overscroll
        float flingDistance = (float) mScroller.getSplineFlingDistance(-velocity);

        return flingDistance < distance;
    }

    @UnsupportedAppUsage
    private void endDrag() {
        mIsBeingDragged = false;