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

Commit 5e05f481 authored by Matt Casey's avatar Matt Casey Committed by Android (Google) Code Review
Browse files

Merge changes I16e9e3d7,I546ae4a7 into sc-dev

* changes:
  Add Save button to long screenshot
  Honor crop bounds for long screenshot bitmap export
parents 7ba56c57 df8b25f9
Loading
Loading
Loading
Loading
+54 −48
Original line number Diff line number Diff line
@@ -22,35 +22,82 @@
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Button
        android:id="@+id/save"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/save"
        app:layout_constraintEnd_toStartOf="@id/cancel"
        app:layout_constraintHorizontal_chainStyle="packed"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toTopOf="@id/guideline" />

    <Button
        android:id="@+id/cancel"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/cancel"
        app:layout_constraintEnd_toStartOf="@id/edit"
        app:layout_constraintStart_toEndOf="@id/save"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toTopOf="@id/guideline" />

    <Button
        android:id="@+id/edit"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/screenshot_edit_label"
        app:layout_constraintEnd_toStartOf="@id/share"
        app:layout_constraintStart_toEndOf="@id/cancel"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toTopOf="@id/guideline" />

    <Button
        android:id="@+id/share"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@*android:string/share"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@+id/edit"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toTopOf="@id/guideline" />

    <androidx.constraintlayout.widget.Guideline
        android:id="@+id/guideline"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        app:layout_constraintGuide_percent="0.1" />

    <ImageView
        android:id="@+id/preview"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginVertical="8dp"
        android:layout_marginBottom="24dp"
        android:layout_marginHorizontal="48dp"
        android:adjustViewBounds="true"
        app:layout_constrainedHeight="true"
        app:layout_constrainedWidth="true"
        app:layout_constraintBottom_toBottomOf="@id/guideline"
        app:layout_constraintTop_toBottomOf="@id/guideline"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        tools:background="?android:colorBackground"
        tools:minHeight="100dp"
        tools:minWidth="100dp" />

    <com.android.systemui.screenshot.CropView
        android:id="@+id/crop_view"
        android:visibility="gone"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginVertical="8dp"
        android:layout_marginBottom="24dp"
        app:layout_constrainedHeight="true"
        app:layout_constrainedWidth="true"
        app:layout_constraintBottom_toBottomOf="@id/guideline"
        app:layout_constraintTop_toBottomOf="@id/guideline"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:handleThickness="3dp"
        app:handleColor="@*android:color/accent_device_default"
        app:scrimColor="#9444"
@@ -58,46 +105,5 @@
        tools:minHeight="100dp"
        tools:minWidth="100dp" />

    <androidx.constraintlayout.widget.Guideline
        android:id="@+id/guideline"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        app:layout_constraintGuide_percent="0.9" />

    <Button
        android:id="@+id/close"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="8dp"
        android:text="Close"
        app:layout_constraintEnd_toStartOf="@+id/edit"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintHorizontal_chainStyle="packed"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="@+id/guideline" />

    <Button
        android:id="@+id/edit"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="8dp"
        android:text="Edit"
        app:layout_constraintEnd_toStartOf="@+id/share"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toEndOf="@+id/close"
        app:layout_constraintTop_toTopOf="@+id/guideline" />

    <Button
        android:id="@+id/share"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="8dp"
        android:text="Share"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toEndOf="@+id/edit"
        app:layout_constraintTop_toTopOf="@+id/guideline" />

</androidx.constraintlayout.widget.ConstraintLayout>
+13 −1
Original line number Diff line number Diff line
@@ -108,6 +108,15 @@ class ImageTileSet {
    }

    Bitmap toBitmap() {
        return toBitmap(new Rect(0, 0, getWidth(), getHeight()));
    }

    /**
     * @param bounds Selected portion of the tile set's bounds (equivalent to tile bounds coord
     *               space). For example, to get the whole doc, use Rect(0, 0, getWidth(),
     *               getHeight()).
     */
    Bitmap toBitmap(Rect bounds) {
        if (mTiles.isEmpty()) {
            return null;
        }
@@ -115,6 +124,9 @@ class ImageTileSet {
        output.setPosition(0, 0, getWidth(), getHeight());
        RecordingCanvas canvas = output.beginRecording();
        canvas.translate(-getLeft(), -getTop());
        // Additional translation to account for the requested bounds
        canvas.translate(-bounds.left, -bounds.top);
        canvas.clipRect(bounds);
        for (ImageTile tile : mTiles) {
            canvas.save();
            canvas.translate(tile.getLeft(), tile.getTop());
@@ -122,7 +134,7 @@ class ImageTileSet {
            canvas.restore();
        }
        output.endRecording();
        return HardwareRenderer.createHardwareBitmap(output, getWidth(), getHeight());
        return HardwareRenderer.createHardwareBitmap(output, bounds.width(), bounds.height());
    }

    int getLeft() {
+31 −23
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import android.annotation.UiThread;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.graphics.Rect;
import android.net.Uri;
import android.os.UserHandle;
import android.text.TextUtils;
@@ -54,7 +55,8 @@ public class ScrollCaptureController implements OnComputeInternalInsetsListener
    // TODO: Support saving without additional action.
    private enum PendingAction {
        SHARE,
        EDIT
        EDIT,
        SAVE
    }

    public static final int MAX_PAGES = 5;
@@ -74,9 +76,11 @@ public class ScrollCaptureController implements OnComputeInternalInsetsListener
    private RequestCallback mCallback;
    private Window mWindow;
    private ImageView mPreview;
    private View mClose;
    private View mSave;
    private View mCancel;
    private View mEdit;
    private View mShare;
    private CropView mCropView;

    public ScrollCaptureController(Context context, Connection connection, Executor uiExecutor,
            Executor bgExecutor, ImageExporter exporter, UiEventLogger uiEventLogger) {
@@ -111,11 +115,14 @@ public class ScrollCaptureController implements OnComputeInternalInsetsListener
                .addOnComputeInternalInsetsListener(this);
        mPreview = findViewById(R.id.preview);

        mClose = findViewById(R.id.close);
        mSave = findViewById(R.id.save);
        mCancel = findViewById(R.id.cancel);
        mEdit = findViewById(R.id.edit);
        mShare = findViewById(R.id.share);
        mCropView = findViewById(R.id.crop_view);

        mClose.setOnClickListener(this::onClicked);
        mSave.setOnClickListener(this::onClicked);
        mCancel.setOnClickListener(this::onClicked);
        mEdit.setOnClickListener(this::onClicked);
        mShare.setOnClickListener(this::onClicked);

@@ -130,7 +137,8 @@ public class ScrollCaptureController implements OnComputeInternalInsetsListener
    }

    void disableButtons() {
        mClose.setEnabled(false);
        mSave.setEnabled(false);
        mCancel.setEnabled(false);
        mEdit.setEnabled(false);
        mShare.setEnabled(false);
    }
@@ -139,19 +147,17 @@ public class ScrollCaptureController implements OnComputeInternalInsetsListener
        Log.d(TAG, "button clicked!");

        int id = v.getId();
        if (id == R.id.close) {
        v.setPressed(true);
        disableButtons();
        if (id == R.id.save) {
            startExport(PendingAction.SAVE);
        } else if (id == R.id.cancel) {
            doFinish();
        } else if (id == R.id.edit) {
            mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_LONG_SCREENSHOT_EDIT);
            v.setPressed(true);
            disableButtons();
            startExport(PendingAction.EDIT);
        } else if (id == R.id.share) {
            mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_LONG_SCREENSHOT_SHARE);
            v.setPressed(true);
            disableButtons();
            startExport(PendingAction.SHARE);
        }
    }
@@ -165,8 +171,13 @@ public class ScrollCaptureController implements OnComputeInternalInsetsListener
    }

    private void startExport(PendingAction action) {
        Rect croppedPortion = new Rect(
                0,
                (int) (mImageTileSet.getHeight() * mCropView.getTopBoundary()),
                mImageTileSet.getWidth(),
                (int) (mImageTileSet.getHeight() * mCropView.getBottomBoundary()));
        ListenableFuture<ImageExporter.Result> exportFuture = mImageExporter.export(
                mBgExecutor, mRequestId, mImageTileSet.toBitmap(), mCaptureTime);
                mBgExecutor, mRequestId, mImageTileSet.toBitmap(croppedPortion), mCaptureTime);
        exportFuture.addListener(() -> {
            try {
                ImageExporter.Result result = exportFuture.get();
@@ -191,24 +202,21 @@ public class ScrollCaptureController implements OnComputeInternalInsetsListener
        }
        intent.setType("image/png");
        intent.setData(uri);
        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
        Intent sharingChooserIntent = Intent.createChooser(intent, null)
                .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK)
                .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK
                | Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);

        mContext.startActivityAsUser(sharingChooserIntent, UserHandle.CURRENT);
        mContext.startActivityAsUser(intent, UserHandle.CURRENT);
    }

    private void doShare(Uri uri) {
        Intent intent = new Intent(Intent.ACTION_SEND);
        intent.setType("image/png");
        intent.setData(uri);
        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK
                | Intent.FLAG_GRANT_READ_URI_PERMISSION);
        Intent sharingChooserIntent = Intent.createChooser(intent, null)
                .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK)
                .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
                .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK
                        | Intent.FLAG_GRANT_READ_URI_PERMISSION);

        mContext.startActivityAsUser(sharingChooserIntent, UserHandle.CURRENT);
    }