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