1 /* 2 * Copyright (C) 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.systemui.screenshot 18 19 import android.net.Uri 20 import android.os.UserManager 21 import android.util.Log 22 import android.view.WindowManager 23 import com.android.internal.logging.UiEventLogger 24 import com.android.systemui.dagger.qualifiers.Main 25 import com.android.systemui.res.R 26 import com.google.common.util.concurrent.ListenableFuture 27 import java.util.UUID 28 import java.util.concurrent.Executor 29 import java.util.concurrent.Executors 30 import java.util.function.Consumer 31 import javax.inject.Inject 32 33 /** 34 * A ScreenshotHandler that just saves the screenshot and calls back as appropriate, with no UI. 35 * 36 * Basically, ScreenshotController with all the UI bits ripped out. 37 */ 38 class HeadlessScreenshotHandler 39 @Inject 40 constructor( 41 private val imageExporter: ImageExporter, 42 @Main private val mainExecutor: Executor, 43 private val imageCapture: ImageCapture, 44 private val userManager: UserManager, 45 private val uiEventLogger: UiEventLogger, 46 private val notificationsControllerFactory: ScreenshotNotificationsController.Factory, 47 ) : ScreenshotHandler { 48 handleScreenshotnull49 override fun handleScreenshot( 50 screenshot: ScreenshotData, 51 finisher: Consumer<Uri?>, 52 requestCallback: TakeScreenshotService.RequestCallback 53 ) { 54 if (screenshot.type == WindowManager.TAKE_SCREENSHOT_FULLSCREEN) { 55 screenshot.bitmap = imageCapture.captureDisplay(screenshot.displayId, crop = null) 56 } 57 58 if (screenshot.bitmap == null) { 59 Log.e(TAG, "handleScreenshot: Screenshot bitmap was null") 60 notificationsControllerFactory 61 .create(screenshot.displayId) 62 .notifyScreenshotError(R.string.screenshot_failed_to_capture_text) 63 requestCallback.reportError() 64 return 65 } 66 67 val future: ListenableFuture<ImageExporter.Result> = 68 imageExporter.export( 69 Executors.newSingleThreadExecutor(), 70 UUID.randomUUID(), 71 screenshot.bitmap, 72 screenshot.getUserOrDefault(), 73 screenshot.displayId 74 ) 75 future.addListener( 76 { 77 try { 78 val result = future.get() 79 Log.d(TAG, "Saved screenshot: $result") 80 logScreenshotResultStatus(result.uri, screenshot) 81 finisher.accept(result.uri) 82 requestCallback.onFinish() 83 } catch (e: Exception) { 84 Log.d(TAG, "Failed to store screenshot", e) 85 finisher.accept(null) 86 requestCallback.reportError() 87 } 88 }, 89 mainExecutor 90 ) 91 } 92 logScreenshotResultStatusnull93 private fun logScreenshotResultStatus(uri: Uri?, screenshot: ScreenshotData) { 94 if (uri == null) { 95 uiEventLogger.log(ScreenshotEvent.SCREENSHOT_NOT_SAVED, 0, screenshot.packageNameString) 96 notificationsControllerFactory 97 .create(screenshot.displayId) 98 .notifyScreenshotError(R.string.screenshot_failed_to_save_text) 99 } else { 100 uiEventLogger.log(ScreenshotEvent.SCREENSHOT_SAVED, 0, screenshot.packageNameString) 101 if (userManager.isManagedProfile(screenshot.getUserOrDefault().identifier)) { 102 uiEventLogger.log( 103 ScreenshotEvent.SCREENSHOT_SAVED_TO_WORK_PROFILE, 104 0, 105 screenshot.packageNameString 106 ) 107 } 108 } 109 } 110 111 companion object { 112 const val TAG = "HeadlessScreenshotHandler" 113 } 114 } 115