Loading packages/SystemUI/AndroidManifest.xml +1 −0 Original line number Original line Diff line number Diff line Loading @@ -338,6 +338,7 @@ <activity android:name=".screenshot.LongScreenshotActivity" <activity android:name=".screenshot.LongScreenshotActivity" android:theme="@android:style/Theme.DeviceDefault.NoActionBar" android:theme="@android:style/Theme.DeviceDefault.NoActionBar" android:process=":screenshot" android:process=":screenshot" android:exported="false" android:finishOnTaskLaunch="true" /> android:finishOnTaskLaunch="true" /> <activity android:name=".screenrecord.ScreenRecordDialog" <activity android:name=".screenrecord.ScreenRecordDialog" Loading packages/SystemUI/src/com/android/systemui/screenshot/CropView.java +19 −0 Original line number Original line Diff line number Diff line Loading @@ -139,6 +139,25 @@ public class CropView extends View { return super.onTouchEvent(event); return super.onTouchEvent(event); } } /** * Set the given boundary to the given value without animation. */ public void setBoundaryTo(CropBoundary boundary, float value) { switch (boundary) { case TOP: mTopCrop = value; break; case BOTTOM: mBottomCrop = value; break; case NONE: Log.w(TAG, "No boundary selected for animation"); break; } invalidate(); } /** /** * Animate the given boundary to the given value. * Animate the given boundary to the given value. */ */ Loading packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java +158 −39 Original line number Original line Diff line number Diff line Loading @@ -20,8 +20,17 @@ import android.app.Activity; import android.content.ComponentName; import android.content.ComponentName; import android.content.Context; import android.content.Context; import android.content.Intent; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.HardwareRenderer; import android.graphics.RecordingCanvas; import android.graphics.Rect; import android.graphics.RenderNode; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.net.Uri; import android.net.Uri; import android.os.Bundle; import android.os.Bundle; import android.os.SystemClock; import android.os.UserHandle; import android.os.UserHandle; import android.text.TextUtils; import android.text.TextUtils; import android.util.Log; import android.util.Log; Loading @@ -33,6 +42,14 @@ import com.android.systemui.R; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dagger.qualifiers.Main; import com.google.common.util.concurrent.ListenableFuture; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.time.ZonedDateTime; import java.util.UUID; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; import java.util.concurrent.Executor; import javax.inject.Inject; import javax.inject.Inject; Loading @@ -44,9 +61,21 @@ import javax.inject.Inject; public class LongScreenshotActivity extends Activity { public class LongScreenshotActivity extends Activity { private static final String TAG = "LongScreenshotActivity"; private static final String TAG = "LongScreenshotActivity"; private static final String IMAGE_PATH_KEY = "saved-image"; private static final String TOP_BOUNDARY_KEY = "top-boundary"; private static final String BOTTOM_BOUNDARY_KEY = "bottom-boundary"; private final UiEventLogger mUiEventLogger; private final UiEventLogger mUiEventLogger; private final ScrollCaptureController mScrollCaptureController; private final ScrollCaptureController mScrollCaptureController; private final ScrollCaptureClient.Connection mConnection; private final ScrollCaptureClient.Connection mConnection; private final Executor mUiExecutor; private final Executor mBackgroundExecutor; private final ImageExporter mImageExporter; private String mSavedImagePath; // If true, the activity is re-loading an image from storage, which should either succeed and // populate the UI or fail and finish the activity. private boolean mRestoringInstance; private ImageView mPreview; private ImageView mPreview; private View mSave; private View mSave; Loading @@ -69,6 +98,9 @@ public class LongScreenshotActivity extends Activity { @Background Executor bgExecutor, @Background Executor bgExecutor, Context context) { Context context) { mUiEventLogger = uiEventLogger; mUiEventLogger = uiEventLogger; mUiExecutor = mainExecutor; mBackgroundExecutor = bgExecutor; mImageExporter = imageExporter; mScrollCaptureController = new ScrollCaptureController(context, mainExecutor, bgExecutor, mScrollCaptureController = new ScrollCaptureController(context, mainExecutor, bgExecutor, imageExporter); imageExporter); Loading @@ -95,12 +127,42 @@ public class LongScreenshotActivity extends Activity { mCancel.setOnClickListener(this::onClicked); mCancel.setOnClickListener(this::onClicked); mEdit.setOnClickListener(this::onClicked); mEdit.setOnClickListener(this::onClicked); mShare.setOnClickListener(this::onClicked); mShare.setOnClickListener(this::onClicked); if (savedInstanceState != null) { String imagePath = savedInstanceState.getString(IMAGE_PATH_KEY); if (!TextUtils.isEmpty(imagePath)) { mRestoringInstance = true; mBackgroundExecutor.execute(() -> { Bitmap bitmap = BitmapFactory.decodeFile(imagePath); if (bitmap == null) { Log.e(TAG, "Failed to read bitmap from " + imagePath); finishAndRemoveTask(); } else { runOnUiThread(() -> { BitmapDrawable drawable = new BitmapDrawable(getResources(), bitmap); mPreview.setImageDrawable(drawable); mMagnifierView.setDrawable(drawable, bitmap.getWidth(), bitmap.getHeight()); mCropView.setBoundaryTo(CropView.CropBoundary.TOP, savedInstanceState.getFloat(TOP_BOUNDARY_KEY, 0f)); mCropView.setBoundaryTo(CropView.CropBoundary.BOTTOM, savedInstanceState.getFloat(BOTTOM_BOUNDARY_KEY, 1f)); mRestoringInstance = false; // Reuse the same path for subsequent restoration. mSavedImagePath = imagePath; Log.d(TAG, "Loaded bitmap from " + imagePath); }); } }); } } } } @Override @Override public void onStart() { public void onStart() { super.onStart(); super.onStart(); if (mPreview.getDrawable() == null) { if (mPreview.getDrawable() == null && !mRestoringInstance) { if (mConnection == null) { if (mConnection == null) { Log.e(TAG, "Failed to get scroll capture connection, bailing out"); Log.e(TAG, "Failed to get scroll capture connection, bailing out"); finishAndRemoveTask(); finishAndRemoveTask(); Loading @@ -110,6 +172,24 @@ public class LongScreenshotActivity extends Activity { } } } } @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putString(IMAGE_PATH_KEY, mSavedImagePath); outState.putFloat(TOP_BOUNDARY_KEY, mCropView.getTopBoundary()); outState.putFloat(BOTTOM_BOUNDARY_KEY, mCropView.getBottomBoundary()); } @Override protected void onDestroy() { super.onDestroy(); if (isFinishing() && !TextUtils.isEmpty(mSavedImagePath)) { Log.d(TAG, "Deleting " + mSavedImagePath); File file = new File(mSavedImagePath); file.delete(); } } private void setButtonsEnabled(boolean enabled) { private void setButtonsEnabled(boolean enabled) { mSave.setEnabled(enabled); mSave.setEnabled(enabled); mCancel.setEnabled(enabled); mCancel.setEnabled(enabled); Loading Loading @@ -161,31 +241,68 @@ public class LongScreenshotActivity extends Activity { } } private void startExport(PendingAction action) { private void startExport(PendingAction action) { mScrollCaptureController.startExport(mCropView.getTopBoundary(), Drawable drawable = mPreview.getDrawable(); mCropView.getBottomBoundary(), new ScrollCaptureController.ExportCallback() { @Override public void onError() { Log.e(TAG, "Error exporting image data."); setButtonsEnabled(true); } @Override Rect croppedPortion = new Rect( public void onExportComplete(Uri outputUri) { 0, (int) (drawable.getIntrinsicHeight() * mCropView.getTopBoundary()), drawable.getIntrinsicWidth(), (int) (drawable.getIntrinsicHeight() * mCropView.getBottomBoundary())); ListenableFuture<ImageExporter.Result> exportFuture = mImageExporter.export( mBackgroundExecutor, UUID.randomUUID(), getBitmap(croppedPortion, drawable), ZonedDateTime.now()); exportFuture.addListener(() -> { try { ImageExporter.Result result = exportFuture.get(); setButtonsEnabled(true); setButtonsEnabled(true); switch (action) { switch (action) { case EDIT: case EDIT: doEdit(outputUri); doEdit(result.uri); break; break; case SHARE: case SHARE: doShare(outputUri); doShare(result.uri); break; break; case SAVE: case SAVE: // Nothing more to do // Nothing more to do finishAndRemoveTask(); finishAndRemoveTask(); break; break; } } } catch (InterruptedException | ExecutionException e) { Log.e(TAG, "failed to export", e); setButtonsEnabled(true); } }, mUiExecutor); } private Bitmap getBitmap(Rect bounds, Drawable drawable) { final RenderNode output = new RenderNode("Bitmap Export"); output.setPosition(0, 0, bounds.width(), bounds.height()); RecordingCanvas canvas = output.beginRecording(); // Translating the canvas instead of setting drawable bounds since the drawable is still // used in the preview. canvas.translate(0, -bounds.top); drawable.draw(canvas); output.endRecording(); return HardwareRenderer.createHardwareBitmap(output, bounds.width(), bounds.height()); } private void saveCacheBitmap(ImageTileSet tileSet) { long startTime = SystemClock.uptimeMillis(); Bitmap bitmap = tileSet.toBitmap(); // TODO(b/181562529) Remove this mPreview.setImageDrawable(tileSet.getDrawable()); try { File file = File.createTempFile("long_screenshot", ".png", null); FileOutputStream stream = new FileOutputStream(file); bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream); stream.flush(); stream.close(); mSavedImagePath = file.getAbsolutePath(); Log.d(TAG, "Saved to " + file.getAbsolutePath() + " in " + (SystemClock.uptimeMillis() - startTime) + "ms"); } catch (IOException e) { Log.e(TAG, "Failed to save bitmap", e); } } }); } } private void doCapture() { private void doCapture() { Loading @@ -193,7 +310,7 @@ public class LongScreenshotActivity extends Activity { new ScrollCaptureController.ScrollCaptureCallback() { new ScrollCaptureController.ScrollCaptureCallback() { @Override @Override public void onError() { public void onError() { Log.e(TAG, "Error!"); Log.e(TAG, "Error capturing long screenshot!"); finishAndRemoveTask(); finishAndRemoveTask(); } } Loading @@ -202,8 +319,10 @@ public class LongScreenshotActivity extends Activity { Log.i(TAG, "Got tiles " + imageTileSet.getWidth() + " x " Log.i(TAG, "Got tiles " + imageTileSet.getWidth() + " x " + imageTileSet.getHeight()); + imageTileSet.getHeight()); mPreview.setImageDrawable(imageTileSet.getDrawable()); mPreview.setImageDrawable(imageTileSet.getDrawable()); mMagnifierView.setImageTileset(imageTileSet); mMagnifierView.setDrawable(imageTileSet.getDrawable(), imageTileSet.getWidth(), imageTileSet.getHeight()); mCropView.animateBoundaryTo(CropView.CropBoundary.BOTTOM, 0.5f); mCropView.animateBoundaryTo(CropView.CropBoundary.BOTTOM, 0.5f); mBackgroundExecutor.execute(() -> saveCacheBitmap(imageTileSet)); } } }); }); } } Loading packages/SystemUI/src/com/android/systemui/screenshot/MagnifierView.java +7 −7 Original line number Original line Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.systemui.screenshot; package com.android.systemui.screenshot; import android.annotation.NonNull; import android.content.Context; import android.content.Context; import android.content.res.TypedArray; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Canvas; Loading Loading @@ -77,13 +78,12 @@ public class MagnifierView extends View implements CropView.CropInteractionListe mCheckerboardPaint.setColor(Color.GRAY); mCheckerboardPaint.setColor(Color.GRAY); } } public void setImageTileset(ImageTileSet tiles) { /** if (tiles != null) { * Set the drawable to be displayed by the magnifier. mDrawable = tiles.getDrawable(); */ mDrawable.setBounds(0, 0, tiles.getWidth(), tiles.getHeight()); public void setDrawable(@NonNull Drawable drawable, int width, int height) { } else { mDrawable = drawable; mDrawable = null; mDrawable.setBounds(0, 0, width, height); } invalidate(); invalidate(); } } Loading packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java +0 −27 Original line number Original line Diff line number Diff line Loading @@ -18,7 +18,6 @@ package com.android.systemui.screenshot; import android.annotation.UiThread; import android.annotation.UiThread; import android.content.Context; import android.content.Context; import android.graphics.Rect; import android.net.Uri; import android.net.Uri; import android.provider.Settings; import android.provider.Settings; import android.util.Log; import android.util.Log; Loading @@ -27,11 +26,8 @@ import com.android.systemui.screenshot.ScrollCaptureClient.CaptureResult; import com.android.systemui.screenshot.ScrollCaptureClient.Connection; import com.android.systemui.screenshot.ScrollCaptureClient.Connection; import com.android.systemui.screenshot.ScrollCaptureClient.Session; import com.android.systemui.screenshot.ScrollCaptureClient.Session; import com.google.common.util.concurrent.ListenableFuture; import java.time.ZonedDateTime; import java.time.ZonedDateTime; import java.util.UUID; import java.util.UUID; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; import java.util.concurrent.Executor; /** /** Loading Loading @@ -89,29 +85,6 @@ public class ScrollCaptureController { connection.start(this::startCapture, maxPages); connection.start(this::startCapture, maxPages); } } /** * @param topCrop [0,1) fraction of the top of the image to be cropped out. * @param bottomCrop (0, 1] fraction to be cropped out, e.g. 0.7 will crop out the bottom 30%. */ public void startExport(float topCrop, float bottomCrop, ExportCallback callback) { Rect croppedPortion = new Rect( 0, (int) (mImageTileSet.getHeight() * topCrop), mImageTileSet.getWidth(), (int) (mImageTileSet.getHeight() * bottomCrop)); ListenableFuture<ImageExporter.Result> exportFuture = mImageExporter.export( mBgExecutor, mRequestId, mImageTileSet.toBitmap(croppedPortion), mCaptureTime); exportFuture.addListener(() -> { try { ImageExporter.Result result = exportFuture.get(); callback.onExportComplete(result.uri); } catch (InterruptedException | ExecutionException e) { Log.e(TAG, "failed to export", e); callback.onError(); } }, mUiExecutor); } private void onCaptureResult(CaptureResult result) { private void onCaptureResult(CaptureResult result) { Log.d(TAG, "onCaptureResult: " + result); Log.d(TAG, "onCaptureResult: " + result); boolean emptyResult = result.captured.height() == 0; boolean emptyResult = result.captured.height() == 0; Loading Loading
packages/SystemUI/AndroidManifest.xml +1 −0 Original line number Original line Diff line number Diff line Loading @@ -338,6 +338,7 @@ <activity android:name=".screenshot.LongScreenshotActivity" <activity android:name=".screenshot.LongScreenshotActivity" android:theme="@android:style/Theme.DeviceDefault.NoActionBar" android:theme="@android:style/Theme.DeviceDefault.NoActionBar" android:process=":screenshot" android:process=":screenshot" android:exported="false" android:finishOnTaskLaunch="true" /> android:finishOnTaskLaunch="true" /> <activity android:name=".screenrecord.ScreenRecordDialog" <activity android:name=".screenrecord.ScreenRecordDialog" Loading
packages/SystemUI/src/com/android/systemui/screenshot/CropView.java +19 −0 Original line number Original line Diff line number Diff line Loading @@ -139,6 +139,25 @@ public class CropView extends View { return super.onTouchEvent(event); return super.onTouchEvent(event); } } /** * Set the given boundary to the given value without animation. */ public void setBoundaryTo(CropBoundary boundary, float value) { switch (boundary) { case TOP: mTopCrop = value; break; case BOTTOM: mBottomCrop = value; break; case NONE: Log.w(TAG, "No boundary selected for animation"); break; } invalidate(); } /** /** * Animate the given boundary to the given value. * Animate the given boundary to the given value. */ */ Loading
packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java +158 −39 Original line number Original line Diff line number Diff line Loading @@ -20,8 +20,17 @@ import android.app.Activity; import android.content.ComponentName; import android.content.ComponentName; import android.content.Context; import android.content.Context; import android.content.Intent; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.HardwareRenderer; import android.graphics.RecordingCanvas; import android.graphics.Rect; import android.graphics.RenderNode; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.net.Uri; import android.net.Uri; import android.os.Bundle; import android.os.Bundle; import android.os.SystemClock; import android.os.UserHandle; import android.os.UserHandle; import android.text.TextUtils; import android.text.TextUtils; import android.util.Log; import android.util.Log; Loading @@ -33,6 +42,14 @@ import com.android.systemui.R; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dagger.qualifiers.Main; import com.google.common.util.concurrent.ListenableFuture; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.time.ZonedDateTime; import java.util.UUID; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; import java.util.concurrent.Executor; import javax.inject.Inject; import javax.inject.Inject; Loading @@ -44,9 +61,21 @@ import javax.inject.Inject; public class LongScreenshotActivity extends Activity { public class LongScreenshotActivity extends Activity { private static final String TAG = "LongScreenshotActivity"; private static final String TAG = "LongScreenshotActivity"; private static final String IMAGE_PATH_KEY = "saved-image"; private static final String TOP_BOUNDARY_KEY = "top-boundary"; private static final String BOTTOM_BOUNDARY_KEY = "bottom-boundary"; private final UiEventLogger mUiEventLogger; private final UiEventLogger mUiEventLogger; private final ScrollCaptureController mScrollCaptureController; private final ScrollCaptureController mScrollCaptureController; private final ScrollCaptureClient.Connection mConnection; private final ScrollCaptureClient.Connection mConnection; private final Executor mUiExecutor; private final Executor mBackgroundExecutor; private final ImageExporter mImageExporter; private String mSavedImagePath; // If true, the activity is re-loading an image from storage, which should either succeed and // populate the UI or fail and finish the activity. private boolean mRestoringInstance; private ImageView mPreview; private ImageView mPreview; private View mSave; private View mSave; Loading @@ -69,6 +98,9 @@ public class LongScreenshotActivity extends Activity { @Background Executor bgExecutor, @Background Executor bgExecutor, Context context) { Context context) { mUiEventLogger = uiEventLogger; mUiEventLogger = uiEventLogger; mUiExecutor = mainExecutor; mBackgroundExecutor = bgExecutor; mImageExporter = imageExporter; mScrollCaptureController = new ScrollCaptureController(context, mainExecutor, bgExecutor, mScrollCaptureController = new ScrollCaptureController(context, mainExecutor, bgExecutor, imageExporter); imageExporter); Loading @@ -95,12 +127,42 @@ public class LongScreenshotActivity extends Activity { mCancel.setOnClickListener(this::onClicked); mCancel.setOnClickListener(this::onClicked); mEdit.setOnClickListener(this::onClicked); mEdit.setOnClickListener(this::onClicked); mShare.setOnClickListener(this::onClicked); mShare.setOnClickListener(this::onClicked); if (savedInstanceState != null) { String imagePath = savedInstanceState.getString(IMAGE_PATH_KEY); if (!TextUtils.isEmpty(imagePath)) { mRestoringInstance = true; mBackgroundExecutor.execute(() -> { Bitmap bitmap = BitmapFactory.decodeFile(imagePath); if (bitmap == null) { Log.e(TAG, "Failed to read bitmap from " + imagePath); finishAndRemoveTask(); } else { runOnUiThread(() -> { BitmapDrawable drawable = new BitmapDrawable(getResources(), bitmap); mPreview.setImageDrawable(drawable); mMagnifierView.setDrawable(drawable, bitmap.getWidth(), bitmap.getHeight()); mCropView.setBoundaryTo(CropView.CropBoundary.TOP, savedInstanceState.getFloat(TOP_BOUNDARY_KEY, 0f)); mCropView.setBoundaryTo(CropView.CropBoundary.BOTTOM, savedInstanceState.getFloat(BOTTOM_BOUNDARY_KEY, 1f)); mRestoringInstance = false; // Reuse the same path for subsequent restoration. mSavedImagePath = imagePath; Log.d(TAG, "Loaded bitmap from " + imagePath); }); } }); } } } } @Override @Override public void onStart() { public void onStart() { super.onStart(); super.onStart(); if (mPreview.getDrawable() == null) { if (mPreview.getDrawable() == null && !mRestoringInstance) { if (mConnection == null) { if (mConnection == null) { Log.e(TAG, "Failed to get scroll capture connection, bailing out"); Log.e(TAG, "Failed to get scroll capture connection, bailing out"); finishAndRemoveTask(); finishAndRemoveTask(); Loading @@ -110,6 +172,24 @@ public class LongScreenshotActivity extends Activity { } } } } @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putString(IMAGE_PATH_KEY, mSavedImagePath); outState.putFloat(TOP_BOUNDARY_KEY, mCropView.getTopBoundary()); outState.putFloat(BOTTOM_BOUNDARY_KEY, mCropView.getBottomBoundary()); } @Override protected void onDestroy() { super.onDestroy(); if (isFinishing() && !TextUtils.isEmpty(mSavedImagePath)) { Log.d(TAG, "Deleting " + mSavedImagePath); File file = new File(mSavedImagePath); file.delete(); } } private void setButtonsEnabled(boolean enabled) { private void setButtonsEnabled(boolean enabled) { mSave.setEnabled(enabled); mSave.setEnabled(enabled); mCancel.setEnabled(enabled); mCancel.setEnabled(enabled); Loading Loading @@ -161,31 +241,68 @@ public class LongScreenshotActivity extends Activity { } } private void startExport(PendingAction action) { private void startExport(PendingAction action) { mScrollCaptureController.startExport(mCropView.getTopBoundary(), Drawable drawable = mPreview.getDrawable(); mCropView.getBottomBoundary(), new ScrollCaptureController.ExportCallback() { @Override public void onError() { Log.e(TAG, "Error exporting image data."); setButtonsEnabled(true); } @Override Rect croppedPortion = new Rect( public void onExportComplete(Uri outputUri) { 0, (int) (drawable.getIntrinsicHeight() * mCropView.getTopBoundary()), drawable.getIntrinsicWidth(), (int) (drawable.getIntrinsicHeight() * mCropView.getBottomBoundary())); ListenableFuture<ImageExporter.Result> exportFuture = mImageExporter.export( mBackgroundExecutor, UUID.randomUUID(), getBitmap(croppedPortion, drawable), ZonedDateTime.now()); exportFuture.addListener(() -> { try { ImageExporter.Result result = exportFuture.get(); setButtonsEnabled(true); setButtonsEnabled(true); switch (action) { switch (action) { case EDIT: case EDIT: doEdit(outputUri); doEdit(result.uri); break; break; case SHARE: case SHARE: doShare(outputUri); doShare(result.uri); break; break; case SAVE: case SAVE: // Nothing more to do // Nothing more to do finishAndRemoveTask(); finishAndRemoveTask(); break; break; } } } catch (InterruptedException | ExecutionException e) { Log.e(TAG, "failed to export", e); setButtonsEnabled(true); } }, mUiExecutor); } private Bitmap getBitmap(Rect bounds, Drawable drawable) { final RenderNode output = new RenderNode("Bitmap Export"); output.setPosition(0, 0, bounds.width(), bounds.height()); RecordingCanvas canvas = output.beginRecording(); // Translating the canvas instead of setting drawable bounds since the drawable is still // used in the preview. canvas.translate(0, -bounds.top); drawable.draw(canvas); output.endRecording(); return HardwareRenderer.createHardwareBitmap(output, bounds.width(), bounds.height()); } private void saveCacheBitmap(ImageTileSet tileSet) { long startTime = SystemClock.uptimeMillis(); Bitmap bitmap = tileSet.toBitmap(); // TODO(b/181562529) Remove this mPreview.setImageDrawable(tileSet.getDrawable()); try { File file = File.createTempFile("long_screenshot", ".png", null); FileOutputStream stream = new FileOutputStream(file); bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream); stream.flush(); stream.close(); mSavedImagePath = file.getAbsolutePath(); Log.d(TAG, "Saved to " + file.getAbsolutePath() + " in " + (SystemClock.uptimeMillis() - startTime) + "ms"); } catch (IOException e) { Log.e(TAG, "Failed to save bitmap", e); } } }); } } private void doCapture() { private void doCapture() { Loading @@ -193,7 +310,7 @@ public class LongScreenshotActivity extends Activity { new ScrollCaptureController.ScrollCaptureCallback() { new ScrollCaptureController.ScrollCaptureCallback() { @Override @Override public void onError() { public void onError() { Log.e(TAG, "Error!"); Log.e(TAG, "Error capturing long screenshot!"); finishAndRemoveTask(); finishAndRemoveTask(); } } Loading @@ -202,8 +319,10 @@ public class LongScreenshotActivity extends Activity { Log.i(TAG, "Got tiles " + imageTileSet.getWidth() + " x " Log.i(TAG, "Got tiles " + imageTileSet.getWidth() + " x " + imageTileSet.getHeight()); + imageTileSet.getHeight()); mPreview.setImageDrawable(imageTileSet.getDrawable()); mPreview.setImageDrawable(imageTileSet.getDrawable()); mMagnifierView.setImageTileset(imageTileSet); mMagnifierView.setDrawable(imageTileSet.getDrawable(), imageTileSet.getWidth(), imageTileSet.getHeight()); mCropView.animateBoundaryTo(CropView.CropBoundary.BOTTOM, 0.5f); mCropView.animateBoundaryTo(CropView.CropBoundary.BOTTOM, 0.5f); mBackgroundExecutor.execute(() -> saveCacheBitmap(imageTileSet)); } } }); }); } } Loading
packages/SystemUI/src/com/android/systemui/screenshot/MagnifierView.java +7 −7 Original line number Original line Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.systemui.screenshot; package com.android.systemui.screenshot; import android.annotation.NonNull; import android.content.Context; import android.content.Context; import android.content.res.TypedArray; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Canvas; Loading Loading @@ -77,13 +78,12 @@ public class MagnifierView extends View implements CropView.CropInteractionListe mCheckerboardPaint.setColor(Color.GRAY); mCheckerboardPaint.setColor(Color.GRAY); } } public void setImageTileset(ImageTileSet tiles) { /** if (tiles != null) { * Set the drawable to be displayed by the magnifier. mDrawable = tiles.getDrawable(); */ mDrawable.setBounds(0, 0, tiles.getWidth(), tiles.getHeight()); public void setDrawable(@NonNull Drawable drawable, int width, int height) { } else { mDrawable = drawable; mDrawable = null; mDrawable.setBounds(0, 0, width, height); } invalidate(); invalidate(); } } Loading
packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java +0 −27 Original line number Original line Diff line number Diff line Loading @@ -18,7 +18,6 @@ package com.android.systemui.screenshot; import android.annotation.UiThread; import android.annotation.UiThread; import android.content.Context; import android.content.Context; import android.graphics.Rect; import android.net.Uri; import android.net.Uri; import android.provider.Settings; import android.provider.Settings; import android.util.Log; import android.util.Log; Loading @@ -27,11 +26,8 @@ import com.android.systemui.screenshot.ScrollCaptureClient.CaptureResult; import com.android.systemui.screenshot.ScrollCaptureClient.Connection; import com.android.systemui.screenshot.ScrollCaptureClient.Connection; import com.android.systemui.screenshot.ScrollCaptureClient.Session; import com.android.systemui.screenshot.ScrollCaptureClient.Session; import com.google.common.util.concurrent.ListenableFuture; import java.time.ZonedDateTime; import java.time.ZonedDateTime; import java.util.UUID; import java.util.UUID; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; import java.util.concurrent.Executor; /** /** Loading Loading @@ -89,29 +85,6 @@ public class ScrollCaptureController { connection.start(this::startCapture, maxPages); connection.start(this::startCapture, maxPages); } } /** * @param topCrop [0,1) fraction of the top of the image to be cropped out. * @param bottomCrop (0, 1] fraction to be cropped out, e.g. 0.7 will crop out the bottom 30%. */ public void startExport(float topCrop, float bottomCrop, ExportCallback callback) { Rect croppedPortion = new Rect( 0, (int) (mImageTileSet.getHeight() * topCrop), mImageTileSet.getWidth(), (int) (mImageTileSet.getHeight() * bottomCrop)); ListenableFuture<ImageExporter.Result> exportFuture = mImageExporter.export( mBgExecutor, mRequestId, mImageTileSet.toBitmap(croppedPortion), mCaptureTime); exportFuture.addListener(() -> { try { ImageExporter.Result result = exportFuture.get(); callback.onExportComplete(result.uri); } catch (InterruptedException | ExecutionException e) { Log.e(TAG, "failed to export", e); callback.onError(); } }, mUiExecutor); } private void onCaptureResult(CaptureResult result) { private void onCaptureResult(CaptureResult result) { Log.d(TAG, "onCaptureResult: " + result); Log.d(TAG, "onCaptureResult: " + result); boolean emptyResult = result.captured.height() == 0; boolean emptyResult = result.captured.height() == 0; Loading