Merge "Screenshot request processor" into tm-qpr-dev
This commit is contained in:
@@ -217,6 +217,10 @@ public class Flags {
|
|||||||
public static final BooleanFlag NEW_BACK_AFFORDANCE =
|
public static final BooleanFlag NEW_BACK_AFFORDANCE =
|
||||||
new BooleanFlag(1203, false /* default */, false /* teamfood */);
|
new BooleanFlag(1203, false /* default */, false /* teamfood */);
|
||||||
|
|
||||||
|
// 1300 - screenshots
|
||||||
|
|
||||||
|
public static final BooleanFlag SCREENSHOT_REQUEST_PROCESSOR = new BooleanFlag(1300, false);
|
||||||
|
|
||||||
// Pay no attention to the reflection behind the curtain.
|
// Pay no attention to the reflection behind the curtain.
|
||||||
// ========================== Curtain ==========================
|
// ========================== Curtain ==========================
|
||||||
// | |
|
// | |
|
||||||
|
|||||||
@@ -0,0 +1,68 @@
|
|||||||
|
/*
|
||||||
|
* 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.net.Uri
|
||||||
|
import android.util.Log
|
||||||
|
import android.view.WindowManager.ScreenshotType
|
||||||
|
import android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN
|
||||||
|
import android.view.WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE
|
||||||
|
import android.view.WindowManager.TAKE_SCREENSHOT_SELECTED_REGION
|
||||||
|
import com.android.internal.util.ScreenshotHelper.HardwareBitmapBundler
|
||||||
|
import com.android.internal.util.ScreenshotHelper.ScreenshotRequest
|
||||||
|
import com.android.systemui.dagger.SysUISingleton
|
||||||
|
import com.android.systemui.screenshot.TakeScreenshotService.RequestCallback
|
||||||
|
import java.util.function.Consumer
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Processes a screenshot request sent from {@link ScreenshotHelper}.
|
||||||
|
*/
|
||||||
|
@SysUISingleton
|
||||||
|
internal class RequestProcessor @Inject constructor(
|
||||||
|
private val controller: ScreenshotController,
|
||||||
|
) {
|
||||||
|
fun processRequest(
|
||||||
|
@ScreenshotType type: Int,
|
||||||
|
onSavedListener: Consumer<Uri>,
|
||||||
|
request: ScreenshotRequest,
|
||||||
|
callback: RequestCallback
|
||||||
|
) {
|
||||||
|
|
||||||
|
if (type == TAKE_SCREENSHOT_PROVIDED_IMAGE) {
|
||||||
|
val image = HardwareBitmapBundler.bundleToHardwareBitmap(request.bitmapBundle)
|
||||||
|
|
||||||
|
controller.handleImageAsScreenshot(
|
||||||
|
image, request.boundsInScreen, request.insets,
|
||||||
|
request.taskId, request.userId, request.topComponent, onSavedListener, callback
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
when (type) {
|
||||||
|
TAKE_SCREENSHOT_FULLSCREEN ->
|
||||||
|
controller.takeScreenshotFullscreen(null, onSavedListener, callback)
|
||||||
|
TAKE_SCREENSHOT_SELECTED_REGION ->
|
||||||
|
controller.takeScreenshotPartial(null, onSavedListener, callback)
|
||||||
|
else -> Log.w(TAG, "Invalid screenshot option: $type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val TAG: String = "RequestProcessor"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -21,6 +21,7 @@ import static android.content.Intent.ACTION_CLOSE_SYSTEM_DIALOGS;
|
|||||||
|
|
||||||
import static com.android.internal.util.ScreenshotHelper.SCREENSHOT_MSG_PROCESS_COMPLETE;
|
import static com.android.internal.util.ScreenshotHelper.SCREENSHOT_MSG_PROCESS_COMPLETE;
|
||||||
import static com.android.internal.util.ScreenshotHelper.SCREENSHOT_MSG_URI;
|
import static com.android.internal.util.ScreenshotHelper.SCREENSHOT_MSG_URI;
|
||||||
|
import static com.android.systemui.flags.Flags.SCREENSHOT_REQUEST_PROCESSOR;
|
||||||
import static com.android.systemui.screenshot.LogConfig.DEBUG_CALLBACK;
|
import static com.android.systemui.screenshot.LogConfig.DEBUG_CALLBACK;
|
||||||
import static com.android.systemui.screenshot.LogConfig.DEBUG_DISMISS;
|
import static com.android.systemui.screenshot.LogConfig.DEBUG_DISMISS;
|
||||||
import static com.android.systemui.screenshot.LogConfig.DEBUG_SERVICE;
|
import static com.android.systemui.screenshot.LogConfig.DEBUG_SERVICE;
|
||||||
@@ -57,6 +58,8 @@ import com.android.internal.logging.UiEventLogger;
|
|||||||
import com.android.internal.util.ScreenshotHelper;
|
import com.android.internal.util.ScreenshotHelper;
|
||||||
import com.android.systemui.R;
|
import com.android.systemui.R;
|
||||||
import com.android.systemui.dagger.qualifiers.Background;
|
import com.android.systemui.dagger.qualifiers.Background;
|
||||||
|
import com.android.systemui.flags.FeatureFlags;
|
||||||
|
import com.android.systemui.flags.FlagListenable.FlagEvent;
|
||||||
|
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
@@ -75,6 +78,8 @@ public class TakeScreenshotService extends Service {
|
|||||||
private final Handler mHandler;
|
private final Handler mHandler;
|
||||||
private final Context mContext;
|
private final Context mContext;
|
||||||
private final @Background Executor mBgExecutor;
|
private final @Background Executor mBgExecutor;
|
||||||
|
private final RequestProcessor mProcessor;
|
||||||
|
private final FeatureFlags mFeatureFlags;
|
||||||
|
|
||||||
private final BroadcastReceiver mCloseSystemDialogs = new BroadcastReceiver() {
|
private final BroadcastReceiver mCloseSystemDialogs = new BroadcastReceiver() {
|
||||||
@Override
|
@Override
|
||||||
@@ -104,7 +109,8 @@ public class TakeScreenshotService extends Service {
|
|||||||
public TakeScreenshotService(ScreenshotController screenshotController, UserManager userManager,
|
public TakeScreenshotService(ScreenshotController screenshotController, UserManager userManager,
|
||||||
DevicePolicyManager devicePolicyManager, UiEventLogger uiEventLogger,
|
DevicePolicyManager devicePolicyManager, UiEventLogger uiEventLogger,
|
||||||
ScreenshotNotificationsController notificationsController, Context context,
|
ScreenshotNotificationsController notificationsController, Context context,
|
||||||
@Background Executor bgExecutor) {
|
@Background Executor bgExecutor, FeatureFlags featureFlags,
|
||||||
|
RequestProcessor processor) {
|
||||||
if (DEBUG_SERVICE) {
|
if (DEBUG_SERVICE) {
|
||||||
Log.d(TAG, "new " + this);
|
Log.d(TAG, "new " + this);
|
||||||
}
|
}
|
||||||
@@ -116,6 +122,9 @@ public class TakeScreenshotService extends Service {
|
|||||||
mNotificationsController = notificationsController;
|
mNotificationsController = notificationsController;
|
||||||
mContext = context;
|
mContext = context;
|
||||||
mBgExecutor = bgExecutor;
|
mBgExecutor = bgExecutor;
|
||||||
|
mFeatureFlags = featureFlags;
|
||||||
|
mFeatureFlags.addListener(SCREENSHOT_REQUEST_PROCESSOR, FlagEvent::requestNoRestart);
|
||||||
|
mProcessor = processor;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -218,6 +227,12 @@ public class TakeScreenshotService extends Service {
|
|||||||
mUiEventLogger.log(ScreenshotEvent.getScreenshotSource(screenshotRequest.getSource()), 0,
|
mUiEventLogger.log(ScreenshotEvent.getScreenshotSource(screenshotRequest.getSource()), 0,
|
||||||
topComponent == null ? "" : topComponent.getPackageName());
|
topComponent == null ? "" : topComponent.getPackageName());
|
||||||
|
|
||||||
|
if (mFeatureFlags.isEnabled(SCREENSHOT_REQUEST_PROCESSOR)) {
|
||||||
|
Log.d(TAG, "handleMessage: Using request processor");
|
||||||
|
mProcessor.processRequest(msg.what, uriConsumer, screenshotRequest, requestCallback);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
switch (msg.what) {
|
switch (msg.what) {
|
||||||
case WindowManager.TAKE_SCREENSHOT_FULLSCREEN:
|
case WindowManager.TAKE_SCREENSHOT_FULLSCREEN:
|
||||||
if (DEBUG_SERVICE) {
|
if (DEBUG_SERVICE) {
|
||||||
|
|||||||
@@ -0,0 +1,106 @@
|
|||||||
|
/*
|
||||||
|
* 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.content.ComponentName
|
||||||
|
import android.graphics.Bitmap
|
||||||
|
import android.graphics.ColorSpace
|
||||||
|
import android.graphics.Insets
|
||||||
|
import android.graphics.Rect
|
||||||
|
import android.hardware.HardwareBuffer
|
||||||
|
import android.net.Uri
|
||||||
|
import android.view.WindowManager
|
||||||
|
import android.view.WindowManager.ScreenshotSource
|
||||||
|
import com.android.internal.util.ScreenshotHelper.HardwareBitmapBundler
|
||||||
|
import com.android.internal.util.ScreenshotHelper.ScreenshotRequest
|
||||||
|
import com.android.systemui.screenshot.TakeScreenshotService.RequestCallback
|
||||||
|
import com.android.systemui.util.mockito.argumentCaptor
|
||||||
|
import com.android.systemui.util.mockito.mock
|
||||||
|
import com.google.common.truth.Truth.assertThat
|
||||||
|
import java.util.function.Consumer
|
||||||
|
import org.junit.Test
|
||||||
|
import org.mockito.Mockito.eq
|
||||||
|
import org.mockito.Mockito.verify
|
||||||
|
import org.mockito.Mockito.isNull
|
||||||
|
|
||||||
|
class RequestProcessorTest {
|
||||||
|
private val controller = mock<ScreenshotController>()
|
||||||
|
private val bitmapCaptor = argumentCaptor<Bitmap>()
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testFullScreenshot() {
|
||||||
|
val request = ScreenshotRequest(ScreenshotSource.SCREENSHOT_KEY_CHORD)
|
||||||
|
val onSavedListener = mock<Consumer<Uri>>()
|
||||||
|
val callback = mock<RequestCallback>()
|
||||||
|
val processor = RequestProcessor(controller)
|
||||||
|
|
||||||
|
processor.processRequest(WindowManager.TAKE_SCREENSHOT_FULLSCREEN, onSavedListener,
|
||||||
|
request, callback)
|
||||||
|
|
||||||
|
verify(controller).takeScreenshotFullscreen(/* topComponent */ isNull(),
|
||||||
|
eq(onSavedListener), eq(callback))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testSelectedRegionScreenshot() {
|
||||||
|
val request = ScreenshotRequest(ScreenshotSource.SCREENSHOT_KEY_CHORD)
|
||||||
|
val onSavedListener = mock<Consumer<Uri>>()
|
||||||
|
val callback = mock<RequestCallback>()
|
||||||
|
val processor = RequestProcessor(controller)
|
||||||
|
|
||||||
|
processor.processRequest(WindowManager.TAKE_SCREENSHOT_SELECTED_REGION, onSavedListener,
|
||||||
|
request, callback)
|
||||||
|
|
||||||
|
verify(controller).takeScreenshotPartial(/* topComponent */ isNull(),
|
||||||
|
eq(onSavedListener), eq(callback))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testProvidedImageScreenshot() {
|
||||||
|
val taskId = 1111
|
||||||
|
val userId = 2222
|
||||||
|
val bounds = Rect(50, 50, 150, 150)
|
||||||
|
val topComponent = ComponentName("test", "test")
|
||||||
|
val processor = RequestProcessor(controller)
|
||||||
|
|
||||||
|
val buffer = HardwareBuffer.create(100, 100, HardwareBuffer.RGBA_8888, 1,
|
||||||
|
HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE)
|
||||||
|
val bitmap = Bitmap.wrapHardwareBuffer(buffer, ColorSpace.get(ColorSpace.Named.SRGB))!!
|
||||||
|
val bitmapBundle = HardwareBitmapBundler.hardwareBitmapToBundle(bitmap)
|
||||||
|
|
||||||
|
val request = ScreenshotRequest(ScreenshotSource.SCREENSHOT_OTHER, bitmapBundle,
|
||||||
|
bounds, Insets.NONE, taskId, userId, topComponent)
|
||||||
|
|
||||||
|
val onSavedListener = mock<Consumer<Uri>>()
|
||||||
|
val callback = mock<RequestCallback>()
|
||||||
|
|
||||||
|
processor.processRequest(WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE, onSavedListener,
|
||||||
|
request, callback)
|
||||||
|
|
||||||
|
verify(controller).handleImageAsScreenshot(
|
||||||
|
bitmapCaptor.capture(), eq(bounds), eq(Insets.NONE), eq(taskId), eq(userId),
|
||||||
|
eq(topComponent), eq(onSavedListener), eq(callback)
|
||||||
|
)
|
||||||
|
|
||||||
|
assertThat(bitmapCaptor.value.equalsHardwareBitmap(bitmap)).isTrue()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Bitmap.equalsHardwareBitmap(bitmap: Bitmap): Boolean {
|
||||||
|
return bitmap.hardwareBuffer == this.hardwareBuffer &&
|
||||||
|
bitmap.colorSpace == this.colorSpace
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user