diff options
| author | 2022-08-04 20:39:07 +0000 | |
|---|---|---|
| committer | 2022-08-04 20:39:07 +0000 | |
| commit | 7c985f904ab2d17b12a1186c41000bf435c49dba (patch) | |
| tree | 8c58c1ae686ff2c1d97af419d88db57184d5f354 | |
| parent | 3e9a910eedca8cf17ed5e1ac7fd4a32757399b8c (diff) | |
| parent | 0a7975e5b0097fa89e8d9a5ef56f4fb24ea76b91 (diff) | |
Merge "Extracts image acquisition to a separate class" into tm-qpr-dev am: a97e6db869 am: 0a7975e5b0
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/19415901
Change-Id: Id5407a534857088b2b516728a797b980455aaa55
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
5 files changed, 197 insertions, 31 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ImageCapture.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ImageCapture.kt new file mode 100644 index 000000000000..39f35a59ff42 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ImageCapture.kt @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.screenshot + +import android.graphics.Bitmap +import android.graphics.Rect + +interface ImageCapture { + + fun captureDisplay(displayId: Int, crop: Rect? = null): Bitmap? + + fun captureTask(taskId: Int): Bitmap? +} diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ImageCaptureImpl.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ImageCaptureImpl.kt new file mode 100644 index 000000000000..258c4360922d --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ImageCaptureImpl.kt @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.screenshot + +import android.app.IActivityTaskManager +import android.graphics.Bitmap +import android.graphics.Rect +import android.hardware.display.DisplayManager +import android.os.IBinder +import android.util.Log +import android.view.DisplayAddress +import android.view.SurfaceControl +import android.view.SurfaceControl.DisplayCaptureArgs +import android.view.SurfaceControl.ScreenshotHardwareBuffer +import androidx.annotation.VisibleForTesting +import javax.inject.Inject + +private const val TAG = "ImageCaptureImpl" + +open class ImageCaptureImpl @Inject constructor( + private val displayManager: DisplayManager, + private val atmService: IActivityTaskManager +) : ImageCapture { + + override fun captureDisplay(displayId: Int, crop: Rect?): Bitmap? { + val width = crop?.width() ?: 0 + val height = crop?.height() ?: 0 + val sourceCrop = crop ?: Rect() + val displayToken = physicalDisplayToken(displayId) ?: return null + val buffer = captureDisplay(displayToken, width, height, sourceCrop) + + return buffer?.asBitmap() + } + + override fun captureTask(taskId: Int): Bitmap? { + val snapshot = atmService.takeTaskSnapshot(taskId) + return Bitmap.wrapHardwareBuffer(snapshot.hardwareBuffer, snapshot.colorSpace) + } + + @VisibleForTesting + open fun physicalDisplayToken(displayId: Int): IBinder? { + val display = displayManager.getDisplay(displayId) + if (display == null) { + Log.e(TAG, "No display with id: $displayId") + return null + } + val address = display.address + if (address !is DisplayAddress.Physical) { + Log.e(TAG, "Display does not have a physical address: $display") + return null + } + return SurfaceControl.getPhysicalDisplayToken(address.physicalDisplayId) + } + + @VisibleForTesting + open fun captureDisplay(displayToken: IBinder, width: Int, height: Int, crop: Rect): ScreenshotHardwareBuffer? { + val captureArgs = DisplayCaptureArgs.Builder(displayToken) + .setSize(width, height) + .setSourceCrop(crop) + .build() + return SurfaceControl.captureDisplay(captureArgs) + } + +} diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java index 82de389060ae..69ee8e8fb8dc 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java @@ -57,14 +57,12 @@ import android.media.AudioSystem; import android.media.MediaPlayer; import android.net.Uri; import android.os.Bundle; -import android.os.IBinder; import android.os.RemoteException; import android.provider.Settings; import android.util.DisplayMetrics; import android.util.Log; import android.util.Pair; import android.view.Display; -import android.view.DisplayAddress; import android.view.IRemoteAnimationFinishedCallback; import android.view.IRemoteAnimationRunner; import android.view.KeyEvent; @@ -72,7 +70,6 @@ import android.view.LayoutInflater; import android.view.RemoteAnimationAdapter; import android.view.RemoteAnimationTarget; import android.view.ScrollCaptureResponse; -import android.view.SurfaceControl; import android.view.View; import android.view.ViewRootImpl; import android.view.ViewTreeObserver; @@ -249,6 +246,7 @@ public class ScreenshotController { private final ScreenshotSmartActions mScreenshotSmartActions; private final UiEventLogger mUiEventLogger; private final ImageExporter mImageExporter; + private final ImageCapture mImageCapture; private final Executor mMainExecutor; private final ExecutorService mBgExecutor; private final BroadcastSender mBroadcastSender; @@ -295,6 +293,7 @@ public class ScreenshotController { ScrollCaptureClient scrollCaptureClient, UiEventLogger uiEventLogger, ImageExporter imageExporter, + ImageCapture imageCapture, @Main Executor mainExecutor, ScrollCaptureController scrollCaptureController, LongScreenshotData longScreenshotHolder, @@ -308,6 +307,7 @@ public class ScreenshotController { mScrollCaptureClient = scrollCaptureClient; mUiEventLogger = uiEventLogger; mImageExporter = imageExporter; + mImageCapture = imageCapture; mMainExecutor = mainExecutor; mScrollCaptureController = scrollCaptureController; mLongScreenshotHolder = longScreenshotHolder; @@ -531,7 +531,7 @@ public class ScreenshotController { // copy the input Rect, since SurfaceControl.screenshot can mutate it Rect screenRect = new Rect(crop); - Bitmap screenshot = captureScreenshot(crop); + Bitmap screenshot = mImageCapture.captureDisplay(DEFAULT_DISPLAY, crop); if (screenshot == null) { Log.e(TAG, "takeScreenshotInternal: Screenshot bitmap was null"); @@ -549,32 +549,6 @@ public class ScreenshotController { ClipboardOverlayController.SELF_PERMISSION); } - private Bitmap captureScreenshot(Rect crop) { - int width = crop.width(); - int height = crop.height(); - Bitmap screenshot = null; - final Display display = getDefaultDisplay(); - final DisplayAddress address = display.getAddress(); - if (!(address instanceof DisplayAddress.Physical)) { - Log.e(TAG, "Skipping Screenshot - Default display does not have a physical address: " - + display); - } else { - final DisplayAddress.Physical physicalAddress = (DisplayAddress.Physical) address; - - final IBinder displayToken = SurfaceControl.getPhysicalDisplayToken( - physicalAddress.getPhysicalDisplayId()); - final SurfaceControl.DisplayCaptureArgs captureArgs = - new SurfaceControl.DisplayCaptureArgs.Builder(displayToken) - .setSourceCrop(crop) - .setSize(width, height) - .build(); - final SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer = - SurfaceControl.captureDisplay(captureArgs); - screenshot = screenshotBuffer == null ? null : screenshotBuffer.asBitmap(); - } - return screenshot; - } - private void saveScreenshot(Bitmap screenshot, Consumer<Uri> finisher, Rect screenRect, Insets screenInsets, ComponentName topComponent, boolean showFlash) { withWindowAttached(() -> @@ -720,7 +694,7 @@ public class ScreenshotController { mScreenshotView.showScrollChip(response.getPackageName(), /* onClick */ () -> { DisplayMetrics displayMetrics = new DisplayMetrics(); getDefaultDisplay().getRealMetrics(displayMetrics); - Bitmap newScreenshot = captureScreenshot( + Bitmap newScreenshot = mImageCapture.captureDisplay(DEFAULT_DISPLAY, new Rect(0, 0, displayMetrics.widthPixels, displayMetrics.heightPixels)); mScreenshotView.prepareScrollingTransition(response, mScreenBitmap, newScreenshot, diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java b/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java index 91ef3c360186..3e44258744f2 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java @@ -18,6 +18,8 @@ package com.android.systemui.screenshot.dagger; import android.app.Service; +import com.android.systemui.screenshot.ImageCapture; +import com.android.systemui.screenshot.ImageCaptureImpl; import com.android.systemui.screenshot.TakeScreenshotService; import dagger.Binds; @@ -37,4 +39,6 @@ public abstract class ScreenshotModule { @ClassKey(TakeScreenshotService.class) public abstract Service bindTakeScreenshotService(TakeScreenshotService service); + @Binds + public abstract ImageCapture bindImageCapture(ImageCaptureImpl capture); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ImageCaptureImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ImageCaptureImplTest.kt new file mode 100644 index 000000000000..ce3f20d4d39d --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ImageCaptureImplTest.kt @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.screenshot + +import android.app.IActivityTaskManager +import android.graphics.Rect +import android.hardware.display.DisplayManager +import android.os.Binder +import android.os.IBinder +import android.testing.AndroidTestingRunner +import android.view.Display +import android.view.SurfaceControl.ScreenshotHardwareBuffer +import com.android.systemui.SysuiTestCase +import com.android.systemui.util.mockito.mock +import com.google.common.truth.Truth.assertThat +import org.junit.Test +import org.junit.runner.RunWith + +/** + * Test the logic within ImageCaptureImpl + */ +@RunWith(AndroidTestingRunner::class) +class ImageCaptureImplTest : SysuiTestCase() { + private val displayManager = mock<DisplayManager>() + private val atmService = mock<IActivityTaskManager>() + private val capture = TestableImageCaptureImpl(displayManager, atmService) + + @Test + fun captureDisplayWithCrop() { + capture.captureDisplay(Display.DEFAULT_DISPLAY, Rect(1, 2, 3, 4)) + assertThat(capture.token).isNotNull() + assertThat(capture.width!!).isEqualTo(2) + assertThat(capture.height!!).isEqualTo(2) + assertThat(capture.crop!!).isEqualTo(Rect(1, 2, 3, 4)) + } + + @Test + fun captureDisplayWithNullCrop() { + capture.captureDisplay(Display.DEFAULT_DISPLAY, null) + assertThat(capture.token).isNotNull() + assertThat(capture.width!!).isEqualTo(0) + assertThat(capture.height!!).isEqualTo(0) + assertThat(capture.crop!!).isEqualTo(Rect()) + } + + class TestableImageCaptureImpl( + displayManager: DisplayManager, + atmService: IActivityTaskManager + ) : + ImageCaptureImpl(displayManager, atmService) { + + var token: IBinder? = null + var width: Int? = null + var height: Int? = null + var crop: Rect? = null + + override fun physicalDisplayToken(displayId: Int): IBinder { + return Binder() + } + + override fun captureDisplay(displayToken: IBinder, width: Int, height: Int, crop: Rect): + ScreenshotHardwareBuffer { + this.token = displayToken + this.width = width + this.height = height + this.crop = crop + return ScreenshotHardwareBuffer(null, null, false, false) + } + } +}
\ No newline at end of file |