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

Commit ce81f3a9 authored by Brad Fitzpatrick's avatar Brad Fitzpatrick
Browse files

StrictMode.Span annotations for ScrollView.

Between the animation system, ListView, and this, we can now account
for the large majority of animations in-flight at any point and annotate
StrictMode violations accordingly.

Note that StrictMode.Span goes out of its way to not allocate memory
and is very light-weight, especially on user builds where it's a
no-op.

I've tested this with the LOG_V debugging in StrictMode and I think
I've gotten all the entry/exit cases of ScrollView's animations.  At
least, I'm not able to get it out-of-sync at least, even with
orientation changes during scroll/fling, etc.  It's possible I missed
a path, but that should show up as excessive false positives during
dropbox uploads if so.

Change-Id: I3b0545111a775d9fa8e171f2a190031f1b56c4c0
parent b43d7589
Loading
Loading
Loading
Loading
+70 −21
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.StrictMode;
import android.util.AttributeSet;
import android.view.FocusFinder;
import android.view.KeyEvent;
@@ -129,6 +130,15 @@ public class ScrollView extends FrameLayout {
     */
    private int mActivePointerId = INVALID_POINTER;

    /**
     * The StrictMode "critical time span" objects to catch animation
     * stutters.  Non-null when a time-sensitive animation is
     * in-flight.  Must call finish() on them when done animating.
     * These are no-ops on user builds.
     */
    private StrictMode.Span mScrollStrictSpan = null;  // aka "drag"
    private StrictMode.Span mFlingStrictSpan = null;

    /**
     * Sentinel value for no current active pointer.
     * Used by {@link #mActivePointerId}.
@@ -437,6 +447,9 @@ public class ScrollView extends FrameLayout {
                if (yDiff > mTouchSlop) {
                    mIsBeingDragged = true;
                    mLastMotionY = y;
                    if (mScrollStrictSpan == null) {
                        mScrollStrictSpan = StrictMode.enterCriticalSpan("ScrollView-scroll");
                    }
                }
                break;
            }
@@ -461,6 +474,9 @@ public class ScrollView extends FrameLayout {
                * being flinged.
                */
                mIsBeingDragged = !mScroller.isFinished();
                if (mIsBeingDragged && mScrollStrictSpan == null) {
                    mScrollStrictSpan = StrictMode.enterCriticalSpan("ScrollView-scroll");
                }
                break;
            }

@@ -512,6 +528,10 @@ public class ScrollView extends FrameLayout {
                 */
                if (!mScroller.isFinished()) {
                    mScroller.abortAnimation();
                    if (mFlingStrictSpan != null) {
                        mFlingStrictSpan.finish();
                        mFlingStrictSpan = null;
                    }
                }

                // Remember where the motion event started
@@ -577,16 +597,7 @@ public class ScrollView extends FrameLayout {
                    }

                    mActivePointerId = INVALID_POINTER;
                    mIsBeingDragged = false;

                    if (mVelocityTracker != null) {
                        mVelocityTracker.recycle();
                        mVelocityTracker = null;
                    }
                    if (mEdgeGlowTop != null) {
                        mEdgeGlowTop.onRelease();
                        mEdgeGlowBottom.onRelease();
                    }
                    endDrag();
                }
                break;
            case MotionEvent.ACTION_CANCEL:
@@ -595,15 +606,7 @@ public class ScrollView extends FrameLayout {
                        invalidate();
                    }
                    mActivePointerId = INVALID_POINTER;
                    mIsBeingDragged = false;
                    if (mVelocityTracker != null) {
                        mVelocityTracker.recycle();
                        mVelocityTracker = null;
                    }
                    if (mEdgeGlowTop != null) {
                        mEdgeGlowTop.onRelease();
                        mEdgeGlowBottom.onRelease();
                    }
                    endDrag();
                }
                break;
            case MotionEvent.ACTION_POINTER_UP:
@@ -1004,6 +1007,10 @@ public class ScrollView extends FrameLayout {
        } else {
            if (!mScroller.isFinished()) {
                mScroller.abortAnimation();
                if (mFlingStrictSpan != null) {
                    mFlingStrictSpan.finish();
                    mFlingStrictSpan = null;
                }
            }
            scrollBy(dx, dy);
        }
@@ -1122,6 +1129,11 @@ public class ScrollView extends FrameLayout {

            // Keep on drawing until the animation has finished.
            postInvalidate();
        } else {
            if (mFlingStrictSpan != null) {
                mFlingStrictSpan.finish();
                mFlingStrictSpan = null;
            }
        }
    }

@@ -1295,6 +1307,20 @@ public class ScrollView extends FrameLayout {
        super.requestLayout();
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();

        if (mScrollStrictSpan != null) {
            mScrollStrictSpan.finish();
            mScrollStrictSpan = null;
        }
        if (mFlingStrictSpan != null) {
            mFlingStrictSpan.finish();
            mFlingStrictSpan = null;
        }
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
@@ -1369,10 +1395,33 @@ public class ScrollView extends FrameLayout {
                mScrollViewMovedFocus = false;
            }

            if (mFlingStrictSpan == null) {
                mFlingStrictSpan = StrictMode.enterCriticalSpan("ScrollView-fling");
            }

            invalidate();
        }
    }

    private void endDrag() {
        mIsBeingDragged = false;

        if (mVelocityTracker != null) {
            mVelocityTracker.recycle();
            mVelocityTracker = null;
        }

        if (mEdgeGlowTop != null) {
            mEdgeGlowTop.onRelease();
            mEdgeGlowBottom.onRelease();
        }

        if (mScrollStrictSpan != null) {
            mScrollStrictSpan.finish();
            mScrollStrictSpan = null;
        }
    }

    /**
     * {@inheritDoc}
     *