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

Commit dc2401a3 authored by Matt Casey's avatar Matt Casey Committed by Automerger Merge Worker
Browse files

Merge "Long screenshot fixes for apps that return less than the requested...

Merge "Long screenshot fixes for apps that return less than the requested rect" into sc-dev am: 18650530

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/15002380

Change-Id: I253a8f117db447f0195ef8e2e30952e8fe606038
parents 59db2c37 18650530
Loading
Loading
Loading
Loading
+19 −47
Original line number Original line Diff line number Diff line
@@ -20,6 +20,7 @@ import android.graphics.Bitmap;
import android.graphics.HardwareRenderer;
import android.graphics.HardwareRenderer;
import android.graphics.RecordingCanvas;
import android.graphics.RecordingCanvas;
import android.graphics.Rect;
import android.graphics.Rect;
import android.graphics.Region;
import android.graphics.RenderNode;
import android.graphics.RenderNode;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.Handler;
@@ -46,7 +47,6 @@ class ImageTileSet {


    private static final String TAG = "ImageTileSet";
    private static final String TAG = "ImageTileSet";


    private CallbackRegistry<OnBoundsChangedListener, ImageTileSet, Rect> mOnBoundsListeners;
    private CallbackRegistry<OnContentChangedListener, ImageTileSet, Rect> mContentListeners;
    private CallbackRegistry<OnContentChangedListener, ImageTileSet, Rect> mContentListeners;


    @Inject
    @Inject
@@ -54,14 +54,6 @@ class ImageTileSet {
        mHandler = handler;
        mHandler = handler;
    }
    }


    interface OnBoundsChangedListener {
        /**
         * Reports an update to the bounding box that contains all active tiles. These are virtual
         * (capture) coordinates which can be either negative or positive.
         */
        void onBoundsChanged(int left, int top, int right, int bottom);
    }

    interface OnContentChangedListener {
    interface OnContentChangedListener {
        /**
        /**
         * Mark as dirty and rebuild display list.
         * Mark as dirty and rebuild display list.
@@ -70,25 +62,9 @@ class ImageTileSet {
    }
    }


    private final List<ImageTile> mTiles = new ArrayList<>();
    private final List<ImageTile> mTiles = new ArrayList<>();
    private final Rect mBounds = new Rect();
    private final Region mRegion = new Region();
    private final Handler mHandler;
    private final Handler mHandler;


    void addOnBoundsChangedListener(OnBoundsChangedListener listener) {
        if (mOnBoundsListeners == null) {
            mOnBoundsListeners = new CallbackRegistry<>(
                    new NotifierCallback<OnBoundsChangedListener, ImageTileSet, Rect>() {
                        @Override
                        public void onNotifyCallback(OnBoundsChangedListener callback,
                                ImageTileSet sender,
                                int arg, Rect newBounds) {
                            callback.onBoundsChanged(newBounds.left, newBounds.top, newBounds.right,
                                    newBounds.bottom);
                        }
                    });
        }
        mOnBoundsListeners.add(listener);
    }

    void addOnContentChangedListener(OnContentChangedListener listener) {
    void addOnContentChangedListener(OnContentChangedListener listener) {
        if (mContentListeners == null) {
        if (mContentListeners == null) {
            mContentListeners = new CallbackRegistry<>(
            mContentListeners = new CallbackRegistry<>(
@@ -110,14 +86,8 @@ class ImageTileSet {
            mHandler.post(() -> addTile(tile));
            mHandler.post(() -> addTile(tile));
            return;
            return;
        }
        }
        final Rect newBounds = new Rect(mBounds);
        final Rect newRect = tile.getLocation();
        mTiles.add(tile);
        mTiles.add(tile);
        newBounds.union(newRect);
        mRegion.op(tile.getLocation(), mRegion, Region.Op.UNION);
        if (!newBounds.equals(mBounds)) {
            mBounds.set(newBounds);
            notifyBoundsChanged(mBounds);
        }
        notifyContentChanged();
        notifyContentChanged();
    }
    }


@@ -127,12 +97,6 @@ class ImageTileSet {
        }
        }
    }
    }


    private void notifyBoundsChanged(Rect bounds) {
        if (mOnBoundsListeners != null) {
            mOnBoundsListeners.notifyCallbacks(this, 0, bounds);
        }
    }

    /**
    /**
     * Returns a drawable to paint the combined contents of the tiles. Drawable dimensions are
     * Returns a drawable to paint the combined contents of the tiles. Drawable dimensions are
     * zero-based and map directly to {@link #getLeft()}, {@link #getTop()}, {@link #getRight()},
     * zero-based and map directly to {@link #getLeft()}, {@link #getTop()}, {@link #getRight()},
@@ -153,6 +117,15 @@ class ImageTileSet {
        return mTiles.size();
        return mTiles.size();
    }
    }


    /**
     * @return the bounding rect around any gaps in the tiles.
     */
    Rect getGaps() {
        Region difference = new Region();
        difference.op(mRegion.getBounds(), mRegion, Region.Op.DIFFERENCE);
        return difference.getBounds();
    }

    ImageTile get(int i) {
    ImageTile get(int i) {
        return mTiles.get(i);
        return mTiles.get(i);
    }
    }
@@ -182,41 +155,40 @@ class ImageTileSet {
    }
    }


    int getLeft() {
    int getLeft() {
        return mBounds.left;
        return mRegion.getBounds().left;
    }
    }


    int getTop() {
    int getTop() {
        return mBounds.top;
        return mRegion.getBounds().top;
    }
    }


    int getRight() {
    int getRight() {
        return mBounds.right;
        return mRegion.getBounds().right;
    }
    }


    int getBottom() {
    int getBottom() {
        return mBounds.bottom;
        return mRegion.getBounds().bottom;
    }
    }


    int getWidth() {
    int getWidth() {
        return mBounds.width();
        return mRegion.getBounds().width();
    }
    }


    int getHeight() {
    int getHeight() {
        return mBounds.height();
        return mRegion.getBounds().height();
    }
    }


    void clear() {
    void clear() {
        if (mTiles.isEmpty()) {
        if (mTiles.isEmpty()) {
            return;
            return;
        }
        }
        mBounds.setEmpty();
        mRegion.setEmpty();
        Iterator<ImageTile> i = mTiles.iterator();
        Iterator<ImageTile> i = mTiles.iterator();
        while (i.hasNext()) {
        while (i.hasNext()) {
            ImageTile next = i.next();
            ImageTile next = i.next();
            next.close();
            next.close();
            i.remove();
            i.remove();
        }
        }
        notifyBoundsChanged(mBounds);
        notifyContentChanged();
        notifyContentChanged();
    }
    }
}
}
+15 −8
Original line number Original line Diff line number Diff line
@@ -56,6 +56,7 @@ import javax.inject.Inject;
public class ScrollCaptureClient {
public class ScrollCaptureClient {
    private static final int TILE_SIZE_PX_MAX = 4 * (1024 * 1024);
    private static final int TILE_SIZE_PX_MAX = 4 * (1024 * 1024);
    private static final int TILES_PER_PAGE = 2; // increase once b/174571735 is addressed
    private static final int TILES_PER_PAGE = 2; // increase once b/174571735 is addressed
    private static final int MAX_TILES = 30;


    @VisibleForTesting
    @VisibleForTesting
    static final int MATCH_ANY_TASK = ActivityTaskManager.INVALID_TASK_ID;
    static final int MATCH_ANY_TASK = ActivityTaskManager.INVALID_TASK_ID;
@@ -83,11 +84,12 @@ public class ScrollCaptureClient {
        int getMaxTiles();
        int getMaxTiles();


        /**
        /**
         * @return the maximum combined capture height for this session, in pixels.
         * Target pixel height for acquisition this session. Session may yield more or less data
         * than this, but acquiring this height is considered sufficient for completion.
         *
         * @return target height in pixels.
         */
         */
        default int getMaxHeight() {
        int getTargetHeight();
            return getMaxTiles() * getTileHeight();
        }


        /**
        /**
         * @return the height of each image tile
         * @return the height of each image tile
@@ -234,11 +236,11 @@ public class ScrollCaptureClient {
        private final int mTileWidth;
        private final int mTileWidth;
        private Rect mRequestRect;
        private Rect mRequestRect;
        private boolean mStarted;
        private boolean mStarted;
        private final int mTargetHeight;


        private ICancellationSignal mCancellationSignal;
        private ICancellationSignal mCancellationSignal;
        private final Rect mWindowBounds;
        private final Rect mWindowBounds;
        private final Rect mBoundsInWindow;
        private final Rect mBoundsInWindow;
        private final int mMaxTiles;


        private Completer<Session> mStartCompleter;
        private Completer<Session> mStartCompleter;
        private Completer<CaptureResult> mTileRequestCompleter;
        private Completer<CaptureResult> mTileRequestCompleter;
@@ -256,7 +258,7 @@ public class ScrollCaptureClient {


            mTileWidth = mBoundsInWindow.width();
            mTileWidth = mBoundsInWindow.width();
            mTileHeight = pxPerTile / mBoundsInWindow.width();
            mTileHeight = pxPerTile / mBoundsInWindow.width();
            mMaxTiles = (int) Math.ceil(maxPages * TILES_PER_PAGE);
            mTargetHeight = (int) (mBoundsInWindow.height() * maxPages);


            if (DEBUG_SCROLL) {
            if (DEBUG_SCROLL) {
                Log.d(TAG, "boundsInWindow: " + mBoundsInWindow);
                Log.d(TAG, "boundsInWindow: " + mBoundsInWindow);
@@ -285,7 +287,7 @@ public class ScrollCaptureClient {


        private void start(Completer<Session> completer) {
        private void start(Completer<Session> completer) {
            mReader = ImageReader.newInstance(mTileWidth, mTileHeight, PixelFormat.RGBA_8888,
            mReader = ImageReader.newInstance(mTileWidth, mTileHeight, PixelFormat.RGBA_8888,
                    mMaxTiles, HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE);
                    MAX_TILES, HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE);
            mStartCompleter = completer;
            mStartCompleter = completer;
            try {
            try {
                mCancellationSignal = mConnection.startCapture(mReader.getSurface(), this);
                mCancellationSignal = mConnection.startCapture(mReader.getSurface(), this);
@@ -409,9 +411,14 @@ public class ScrollCaptureClient {
            return new Rect(mBoundsInWindow);
            return new Rect(mBoundsInWindow);
        }
        }


        @Override
        public int getTargetHeight() {
            return mTargetHeight;
        }

        @Override
        @Override
        public int getMaxTiles() {
        public int getMaxTiles() {
            return mMaxTiles;
            return MAX_TILES;
        }
        }
    }
    }
}
}
+17 −17
Original line number Original line Diff line number Diff line
@@ -199,22 +199,20 @@ public class ScrollCaptureController {
        Log.d(TAG, "onCaptureResult: " + result + " scrolling " + (mScrollingUp ? "UP" : "DOWN")
        Log.d(TAG, "onCaptureResult: " + result + " scrolling " + (mScrollingUp ? "UP" : "DOWN")
                + " finish on boundary: " + mFinishOnBoundary);
                + " finish on boundary: " + mFinishOnBoundary);
        boolean emptyResult = result.captured.height() == 0;
        boolean emptyResult = result.captured.height() == 0;
        boolean partialResult = !emptyResult
                && result.captured.height() < result.requested.height();
        boolean finish = false;


        if (emptyResult) {
        if (emptyResult) {
            // Potentially reached a vertical boundary. Extend in the other direction.
            // Potentially reached a vertical boundary. Extend in the other direction.
            if (mFinishOnBoundary) {
            if (mFinishOnBoundary) {
                Log.d(TAG, "Partial/empty: finished!");
                Log.d(TAG, "Empty: finished!");
                finish = true;
                finishCapture();
                return;
            } else {
            } else {
                // We hit a boundary, clear the tiles, capture everything in the opposite direction,
                // We hit a boundary, clear the tiles, capture everything in the opposite direction,
                // then finish.
                // then finish.
                mImageTileSet.clear();
                mImageTileSet.clear();
                mFinishOnBoundary = true;
                mFinishOnBoundary = true;
                mScrollingUp = !mScrollingUp;
                mScrollingUp = !mScrollingUp;
                Log.d(TAG, "Partial/empty: cleared, switch direction to finish");
                Log.d(TAG, "Empty: cleared, switch direction to finish");
            }
            }
        } else {
        } else {
            // Got a non-empty result, but may already have enough bitmap data now
            // Got a non-empty result, but may already have enough bitmap data now
@@ -223,12 +221,14 @@ public class ScrollCaptureController {
                Log.d(TAG, "Hit max tiles: finished");
                Log.d(TAG, "Hit max tiles: finished");
                // If we ever hit the max tiles, we've got enough bitmap data to finish
                // If we ever hit the max tiles, we've got enough bitmap data to finish
                // (even if we weren't sure we'd finish on this pass).
                // (even if we weren't sure we'd finish on this pass).
                finish = true;
                finishCapture();
                return;
            } else {
            } else {
                if (mScrollingUp && !mFinishOnBoundary) {
                if (mScrollingUp && !mFinishOnBoundary) {
                    // During the initial scroll up, we only want to acquire the portion described
                    // During the initial scroll up, we only want to acquire the portion described
                    // by IDEAL_PORTION_ABOVE.
                    // by IDEAL_PORTION_ABOVE.
                    if (expectedTiles >= mSession.getMaxTiles() * IDEAL_PORTION_ABOVE) {
                    if (mImageTileSet.getHeight() + result.captured.height()
                            >= mSession.getTargetHeight() * IDEAL_PORTION_ABOVE) {
                        Log.d(TAG, "Hit ideal portion above: clear and switch direction");
                        Log.d(TAG, "Hit ideal portion above: clear and switch direction");
                        // We got enough above the start point, now see how far down it can go.
                        // We got enough above the start point, now see how far down it can go.
                        mImageTileSet.clear();
                        mImageTileSet.clear();
@@ -246,15 +246,15 @@ public class ScrollCaptureController {
                + " - " +  mImageTileSet.getRight() + "," + mImageTileSet.getBottom()
                + " - " +  mImageTileSet.getRight() + "," + mImageTileSet.getBottom()
                + " (" + mImageTileSet.getWidth() + "x" + mImageTileSet.getHeight() + ")");
                + " (" + mImageTileSet.getWidth() + "x" + mImageTileSet.getHeight() + ")");



        Rect gapBounds = mImageTileSet.getGaps();
        // Stop when "too tall"
        if (!gapBounds.isEmpty()) {
        if (mImageTileSet.getHeight() > MAX_HEIGHT) {
            Log.d(TAG, "Found gaps in tileset: " + gapBounds + ", requesting " + gapBounds.top);
            Log.d(TAG, "Max height reached.");
            requestNextTile(gapBounds.top);
            finish = true;
            return;
        }
        }


        if (finish) {
        if (mImageTileSet.getHeight() >= mSession.getTargetHeight()) {
            Log.d(TAG, "Stop.");
            Log.d(TAG, "Target height reached.");
            finishCapture();
            finishCapture();
            return;
            return;
        }
        }
@@ -268,8 +268,8 @@ public class ScrollCaptureController {
                    : result.requested.bottom;
                    : result.requested.bottom;
        } else {
        } else {
            nextTop = (mScrollingUp)
            nextTop = (mScrollingUp)
                    ? result.captured.top - mSession.getTileHeight()
                    ? mImageTileSet.getTop() - mSession.getTileHeight()
                    : result.captured.bottom;
                    : mImageTileSet.getBottom();
        }
        }
        requestNextTile(nextTop);
        requestNextTile(nextTop);
    }
    }
+10 −2
Original line number Original line Diff line number Diff line
@@ -57,16 +57,19 @@ class FakeSession implements ScrollCaptureClient.Session {


    private int mScrollDelta;
    private int mScrollDelta;
    private int mPageHeight;
    private int mPageHeight;
    private int mTargetHeight;


    FakeSession(int pageHeight, float maxPages, int tileHeight, int visiblePageTop,
    FakeSession(int pageHeight, float maxPages, int tileHeight, int visiblePageTop,
            int visiblePageBottom, int availableTop, int availableBottom) {
            int visiblePageBottom, int availableTop, int availableBottom,
            int maxTiles) {
        mPageHeight = pageHeight;
        mPageHeight = pageHeight;
        mTileHeight = tileHeight;
        mTileHeight = tileHeight;
        mAvailable = new Rect(0, availableTop, getPageWidth(), availableBottom);
        mAvailable = new Rect(0, availableTop, getPageWidth(), availableBottom);
        mAvailableTop = new Rect(mAvailable);
        mAvailableTop = new Rect(mAvailable);
        mAvailableTop.inset(0, 0, 0, pageHeight);
        mAvailableTop.inset(0, 0, 0, pageHeight);
        mVisiblePage = new Rect(0, visiblePageTop, getPageWidth(), visiblePageBottom);
        mVisiblePage = new Rect(0, visiblePageTop, getPageWidth(), visiblePageBottom);
        mMaxTiles = (int) Math.ceil((pageHeight * maxPages) / mTileHeight);
        mTargetHeight = (int) (pageHeight * maxPages);
        mMaxTiles = maxTiles;
    }
    }


    private static Image mockImage() {
    private static Image mockImage() {
@@ -157,6 +160,11 @@ class FakeSession implements ScrollCaptureClient.Session {
        return mMaxTiles;
        return mMaxTiles;
    }
    }


    @Override
    public int getTargetHeight() {
        return mTargetHeight;
    }

    @Override
    @Override
    public int getTileHeight() {
    public int getTileHeight() {
        return mTileHeight;
        return mTileHeight;
+26 −28
Original line number Original line Diff line number Diff line
@@ -41,21 +41,6 @@ import org.junit.runner.RunWith;
@SmallTest
@SmallTest
@RunWith(AndroidTestingRunner.class)
@RunWith(AndroidTestingRunner.class)
public class FakeSessionTest extends SysuiTestCase {
public class FakeSessionTest extends SysuiTestCase {
    @Test
    public void testMaxTiles() {
        FakeSession session = new FakeSession(
                /* pageHeight */ 100,
                /* maxPages */ 2.25f,
                /* tileHeight */ 10,
                /* visiblePageTop */ 0,
                /* visiblePageBottom */ 100,
                /* availableTop */ -250,
                /* availableBottom */ 250);

        // (pageHeight * maxPages) / tileHeight
        assertEquals("getMaxTiles()", 23, session.getMaxTiles());
    }

    @Test
    @Test
    public void testNonEmptyResult_hasImage() {
    public void testNonEmptyResult_hasImage() {
        FakeSession session = new FakeSession(
        FakeSession session = new FakeSession(
@@ -65,7 +50,8 @@ public class FakeSessionTest extends SysuiTestCase {
                /* visiblePageTop */ 0,
                /* visiblePageTop */ 0,
                /* visiblePageBottom */ 100,
                /* visiblePageBottom */ 100,
                /* availableTop */ 0,
                /* availableTop */ 0,
                /* availableBottom */ 100);
                /* availableBottom */ 100,
                /* max Tiles */ 30);
        ScrollCaptureClient.CaptureResult result = getUnchecked(session.requestTile(0));
        ScrollCaptureClient.CaptureResult result = getUnchecked(session.requestTile(0));
        assertNotNull("result.image", result.image);
        assertNotNull("result.image", result.image);
        assertNotNull("result.image.getHardwareBuffer()", result.image.getHardwareBuffer());
        assertNotNull("result.image.getHardwareBuffer()", result.image.getHardwareBuffer());
@@ -80,7 +66,8 @@ public class FakeSessionTest extends SysuiTestCase {
                /* visiblePageTop */ 0,
                /* visiblePageTop */ 0,
                /* visiblePageBottom */ 100,
                /* visiblePageBottom */ 100,
                /* availableTop */ 0,
                /* availableTop */ 0,
                /* availableBottom */ 100);
                /* availableBottom */ 100,
                /* max Tiles */ 30);
        ScrollCaptureClient.CaptureResult result = getUnchecked(session.requestTile(-100));
        ScrollCaptureClient.CaptureResult result = getUnchecked(session.requestTile(-100));
        assertNull("result.image", result.image);
        assertNull("result.image", result.image);
    }
    }
@@ -94,7 +81,8 @@ public class FakeSessionTest extends SysuiTestCase {
                /* visiblePageTop */ 0,
                /* visiblePageTop */ 0,
                /* visiblePageBottom */ 100,
                /* visiblePageBottom */ 100,
                /* availableTop */ -250,
                /* availableTop */ -250,
                /* availableBottom */ 250);
                /* availableBottom */ 250,
                /* max Tiles */ 30);


        ScrollCaptureClient.CaptureResult result = getUnchecked(session.requestTile(0));
        ScrollCaptureClient.CaptureResult result = getUnchecked(session.requestTile(0));
        assertEquals("requested top", 0, result.requested.top);
        assertEquals("requested top", 0, result.requested.top);
@@ -113,7 +101,8 @@ public class FakeSessionTest extends SysuiTestCase {
                /* visiblePageTop */ 0,
                /* visiblePageTop */ 0,
                /* visiblePageBottom */ 100,
                /* visiblePageBottom */ 100,
                /* availableTop */ -250,
                /* availableTop */ -250,
                /* availableBottom */ 250);
                /* availableBottom */ 250,
                /* max Tiles */ 30);


        ScrollCaptureClient.CaptureResult result = getUnchecked(session.requestTile(90));
        ScrollCaptureClient.CaptureResult result = getUnchecked(session.requestTile(90));
        assertEquals("requested top", 90, result.requested.top);
        assertEquals("requested top", 90, result.requested.top);
@@ -132,7 +121,8 @@ public class FakeSessionTest extends SysuiTestCase {
                /* visiblePageTop */ 0,
                /* visiblePageTop */ 0,
                /* visiblePageBottom */ 100,
                /* visiblePageBottom */ 100,
                /* availableTop */ -250,
                /* availableTop */ -250,
                /* availableBottom */ 250);
                /* availableBottom */ 250,
                /* max Tiles */ 30);


        ScrollCaptureClient.CaptureResult result = getUnchecked(session.requestTile(-100));
        ScrollCaptureClient.CaptureResult result = getUnchecked(session.requestTile(-100));
        assertEquals("requested top", -100, result.requested.top);
        assertEquals("requested top", -100, result.requested.top);
@@ -151,7 +141,8 @@ public class FakeSessionTest extends SysuiTestCase {
                /* visiblePageTop */ 0,
                /* visiblePageTop */ 0,
                /* visiblePageBottom */ 100,
                /* visiblePageBottom */ 100,
                /* availableTop */ -250,
                /* availableTop */ -250,
                /* availableBottom */ 250);
                /* availableBottom */ 250,
                /* max Tiles */ 30);


        ScrollCaptureClient.CaptureResult result = getUnchecked(session.requestTile(150));
        ScrollCaptureClient.CaptureResult result = getUnchecked(session.requestTile(150));
        assertEquals("requested top", 150, result.requested.top);
        assertEquals("requested top", 150, result.requested.top);
@@ -170,7 +161,8 @@ public class FakeSessionTest extends SysuiTestCase {
                /* visiblePageTop */ 0,
                /* visiblePageTop */ 0,
                /* visiblePageBottom */ 100,
                /* visiblePageBottom */ 100,
                /* availableTop */ -100,
                /* availableTop */ -100,
                /* availableBottom */ 100);
                /* availableBottom */ 100,
                /* max Tiles */ 30);


        ScrollCaptureClient.CaptureResult result = getUnchecked(session.requestTile(-125));
        ScrollCaptureClient.CaptureResult result = getUnchecked(session.requestTile(-125));
        assertEquals("requested top", -125, result.requested.top);
        assertEquals("requested top", -125, result.requested.top);
@@ -189,7 +181,8 @@ public class FakeSessionTest extends SysuiTestCase {
                /* visiblePageTop */ 0,
                /* visiblePageTop */ 0,
                /* visiblePageBottom */ 100,
                /* visiblePageBottom */ 100,
                /* availableTop */ -100,
                /* availableTop */ -100,
                /* availableBottom */ 100);
                /* availableBottom */ 100,
                /* max Tiles */ 30);


        ScrollCaptureClient.CaptureResult result = getUnchecked(session.requestTile(75));
        ScrollCaptureClient.CaptureResult result = getUnchecked(session.requestTile(75));
        assertEquals("requested top", 75, result.requested.top);
        assertEquals("requested top", 75, result.requested.top);
@@ -211,7 +204,8 @@ public class FakeSessionTest extends SysuiTestCase {
                /* visiblePageTop */ 25,  // <<--
                /* visiblePageTop */ 25,  // <<--
                /* visiblePageBottom */ 100,
                /* visiblePageBottom */ 100,
                /* availableTop */ -150,
                /* availableTop */ -150,
                /* availableBottom */ 150);
                /* availableBottom */ 150,
                /* max Tiles */ 30);


        ScrollCaptureClient.CaptureResult result = getUnchecked(session.requestTile(-150));
        ScrollCaptureClient.CaptureResult result = getUnchecked(session.requestTile(-150));
        assertEquals("requested top", -150, result.requested.top);
        assertEquals("requested top", -150, result.requested.top);
@@ -233,7 +227,8 @@ public class FakeSessionTest extends SysuiTestCase {
                /* visiblePageTop */ 0,
                /* visiblePageTop */ 0,
                /* visiblePageBottom */ 75,
                /* visiblePageBottom */ 75,
                /* availableTop */ -150,
                /* availableTop */ -150,
                /* availableBottom */ 150);
                /* availableBottom */ 150,
                /* max Tiles */ 30);


        ScrollCaptureClient.CaptureResult result = getUnchecked(session.requestTile(50));
        ScrollCaptureClient.CaptureResult result = getUnchecked(session.requestTile(50));
        assertEquals("requested top", 50, result.requested.top);
        assertEquals("requested top", 50, result.requested.top);
@@ -252,7 +247,8 @@ public class FakeSessionTest extends SysuiTestCase {
                /* visiblePageTop */ 0,
                /* visiblePageTop */ 0,
                /* visiblePageBottom */ 100,
                /* visiblePageBottom */ 100,
                /* availableTop */ -100,
                /* availableTop */ -100,
                /* availableBottom */ 200);
                /* availableBottom */ 200,
                /* max Tiles */ 30);
        ScrollCaptureClient.CaptureResult result = getUnchecked(session.requestTile(-150));
        ScrollCaptureClient.CaptureResult result = getUnchecked(session.requestTile(-150));
        assertTrue("captured rect is empty", result.captured.isEmpty());
        assertTrue("captured rect is empty", result.captured.isEmpty());
    }
    }
@@ -266,7 +262,8 @@ public class FakeSessionTest extends SysuiTestCase {
                /* visiblePageTop */ 0,
                /* visiblePageTop */ 0,
                /* visiblePageBottom */ 100,
                /* visiblePageBottom */ 100,
                /* availableTop */ -100,
                /* availableTop */ -100,
                /* availableBottom */ 200);
                /* availableBottom */ 200,
                /* max Tiles */ 30);
        ScrollCaptureClient.CaptureResult result = getUnchecked(session.requestTile(200));
        ScrollCaptureClient.CaptureResult result = getUnchecked(session.requestTile(200));
        assertTrue("captured rect is empty", result.captured.isEmpty());
        assertTrue("captured rect is empty", result.captured.isEmpty());
    }
    }
@@ -280,7 +277,8 @@ public class FakeSessionTest extends SysuiTestCase {
                /* visiblePageTop */ 60,  // <<---
                /* visiblePageTop */ 60,  // <<---
                /* visiblePageBottom */ 0,
                /* visiblePageBottom */ 0,
                /* availableTop */ -150,
                /* availableTop */ -150,
                /* availableBottom */ 150);
                /* availableBottom */ 150,
                /* max Tiles */ 30);


        ScrollCaptureClient.CaptureResult result = getUnchecked(session.requestTile(0));
        ScrollCaptureClient.CaptureResult result = getUnchecked(session.requestTile(0));
        assertEquals("requested top", 0, result.requested.top);
        assertEquals("requested top", 0, result.requested.top);
Loading