Merge "Screenshot request processor" into tm-qpr-dev

This commit is contained in:
Mark Renouf
2022-08-04 14:46:29 +00:00
committed by Android (Google) Code Review
4 changed files with 194 additions and 1 deletions

View File

@@ -217,6 +217,10 @@ public class Flags {
public static final BooleanFlag NEW_BACK_AFFORDANCE =
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.
// ========================== Curtain ==========================
// | |

View File

@@ -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"
}
}

View File

@@ -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_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_DISMISS;
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.systemui.R;
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.function.Consumer;
@@ -75,6 +78,8 @@ public class TakeScreenshotService extends Service {
private final Handler mHandler;
private final Context mContext;
private final @Background Executor mBgExecutor;
private final RequestProcessor mProcessor;
private final FeatureFlags mFeatureFlags;
private final BroadcastReceiver mCloseSystemDialogs = new BroadcastReceiver() {
@Override
@@ -104,7 +109,8 @@ public class TakeScreenshotService extends Service {
public TakeScreenshotService(ScreenshotController screenshotController, UserManager userManager,
DevicePolicyManager devicePolicyManager, UiEventLogger uiEventLogger,
ScreenshotNotificationsController notificationsController, Context context,
@Background Executor bgExecutor) {
@Background Executor bgExecutor, FeatureFlags featureFlags,
RequestProcessor processor) {
if (DEBUG_SERVICE) {
Log.d(TAG, "new " + this);
}
@@ -116,6 +122,9 @@ public class TakeScreenshotService extends Service {
mNotificationsController = notificationsController;
mContext = context;
mBgExecutor = bgExecutor;
mFeatureFlags = featureFlags;
mFeatureFlags.addListener(SCREENSHOT_REQUEST_PROCESSOR, FlagEvent::requestNoRestart);
mProcessor = processor;
}
@Override
@@ -218,6 +227,12 @@ public class TakeScreenshotService extends Service {
mUiEventLogger.log(ScreenshotEvent.getScreenshotSource(screenshotRequest.getSource()), 0,
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) {
case WindowManager.TAKE_SCREENSHOT_FULLSCREEN:
if (DEBUG_SERVICE) {

View File

@@ -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
}
}