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

Commit 5fa550db authored by Mark Renouf's avatar Mark Renouf
Browse files

Long screenshots: allow capture in reverse direction

Captures in the opposite direction if an edge is detected
early. This allows more useful behavior in "transcript"
style UI where the content grows upward and the user is
typically scrolled near the bottom edge.

Bug: 179378294
Test: manually, in messages or chat
Change-Id: I5c765b4e103990f428fcd24e26bd09536d613d96
parent 97e7d1a0
Loading
Loading
Loading
Loading
+84 −35
Original line number Diff line number Diff line
@@ -34,6 +34,7 @@ import android.widget.ImageView;

import com.android.internal.logging.UiEventLogger;
import com.android.systemui.R;
import com.android.systemui.screenshot.ScrollCaptureClient.CaptureResult;
import com.android.systemui.screenshot.ScrollCaptureClient.Connection;
import com.android.systemui.screenshot.ScrollCaptureClient.Session;
import com.android.systemui.screenshot.TakeScreenshotService.RequestCallback;
@@ -44,7 +45,6 @@ import java.time.ZonedDateTime;
import java.util.UUID;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.function.Consumer;

/**
 * Interaction controller between the UI and ScrollCaptureClient.
@@ -52,6 +52,14 @@ import java.util.function.Consumer;
public class ScrollCaptureController implements OnComputeInternalInsetsListener {
    private static final String TAG = "ScrollCaptureController";

    private static final int UP = -1;
    private static final int DOWN = 1;

    private int mDirection = DOWN;
    private boolean mAtBottomEdge;
    private boolean mAtTopEdge;
    private Session mSession;

    // TODO: Support saving without additional action.
    private enum PendingAction {
        SHARE,
@@ -232,41 +240,82 @@ public class ScrollCaptureController implements OnComputeInternalInsetsListener
        return mWindow.findViewById(res);
    }

    private void startCapture(Session session) {
        Log.d(TAG, "startCapture");
        Consumer<ScrollCaptureClient.CaptureResult> consumer =
                new Consumer<ScrollCaptureClient.CaptureResult>() {

                    int mFrameCount = 0;
                    int mTop = 0;

                    @Override
                    public void accept(ScrollCaptureClient.CaptureResult result) {
                        mFrameCount++;

                        boolean emptyFrame = result.captured.height() == 0;
                        if (!emptyFrame) {
                            ImageTile tile = new ImageTile(result.image, result.captured);
                            Log.d(TAG, "Adding tile: " + tile);
                            mImageTileSet.addTile(tile);
                            Log.d(TAG, "New dimens: w=" + mImageTileSet.getWidth() + ", "
                                    + "h=" + mImageTileSet.getHeight());

    private void onCaptureResult(CaptureResult result) {
        Log.d(TAG, "onCaptureResult: " + result);
        boolean emptyResult = result.captured.height() == 0;
        boolean partialResult = !emptyResult
                && result.captured.height() < result.requested.height();
        boolean finish = false;

        if (partialResult) {
            // Potentially reached a vertical boundary. Extend in the other direction.
            switch (mDirection) {
                case DOWN:
                    Log.d(TAG, "Reached bottom edge.");
                    mAtBottomEdge = true;
                    mDirection = UP;
                    break;
                case UP:
                    Log.d(TAG, "Reached top edge.");
                    mAtTopEdge = true;
                    mDirection = DOWN;
                    break;
            }

            if (mAtTopEdge && mAtBottomEdge) {
                Log.d(TAG, "Reached both top and bottom edge, ending.");
                finish = true;
            } else {
                // only reverse if the edge was relatively close to the starting point
                if (mImageTileSet.getHeight() < mSession.getPageHeight() * 3) {
                    Log.d(TAG, "Restarting in reverse direction.");

                    // Because of temporary limitations, we cannot just jump to the opposite edge
                    // and continue there. Instead, clear the results and start over capturing from
                    // here in the other direction.
                    mImageTileSet.clear();
                } else {
                    Log.d(TAG, "Capture is tall enough, stopping here.");
                    finish = true;
                }
            }
        }

        if (!emptyResult) {
            mImageTileSet.addTile(new ImageTile(result.image, result.captured));
        }

        Log.d(TAG, "bounds: " + mImageTileSet.getLeft() + "," + mImageTileSet.getTop()
                + " - " +  mImageTileSet.getRight() + "," + mImageTileSet.getBottom()
                + " (" + mImageTileSet.getWidth() + "x" + mImageTileSet.getHeight() + ")");

                        if (emptyFrame || mFrameCount >= MAX_PAGES
                                || mTop + session.getTileHeight() > MAX_HEIGHT) {

        // Stop when "too tall"
        if (mImageTileSet.size() >= mSession.getMaxTiles()
                || mImageTileSet.getHeight() > MAX_HEIGHT) {
            Log.d(TAG, "Max height and/or tile count reached.");
            finish = true;
        }

        if (finish) {
            Session session = mSession;
            mSession = null;
            Log.d(TAG, "Stop.");
            mUiExecutor.execute(() -> afterCaptureComplete(session));
            return;
        }
                        mTop += result.captured.height();
                        session.requestTile(mTop, /* consumer */ this);

        int nextTop = (mDirection == DOWN) ? result.captured.bottom
                : result.captured.top - mSession.getTileHeight();
        Log.d(TAG, "requestTile: " + nextTop);
        mSession.requestTile(nextTop, /* consumer */ this::onCaptureResult);
    }
                };

        // fire it up!
        session.requestTile(0, consumer);
    };
    private void startCapture(Session session) {
        mSession = session;
        session.requestTile(0, this::onCaptureResult);
    }

    @UiThread
    void afterCaptureComplete(Session session) {