1 /* <lambda>null2 * 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.app.assist.AssistContent 20 import android.content.Context 21 import android.net.Uri 22 import android.util.Log 23 import androidx.appcompat.content.res.AppCompatResources 24 import com.android.internal.logging.UiEventLogger 25 import com.android.systemui.dagger.qualifiers.Application 26 import com.android.systemui.log.DebugLogger.debugLog 27 import com.android.systemui.res.R 28 import com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_EDIT_TAPPED 29 import com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_PREVIEW_TAPPED 30 import com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_SHARE_TAPPED 31 import com.android.systemui.screenshot.ui.viewmodel.ActionButtonAppearance 32 import com.android.systemui.screenshot.ui.viewmodel.PreviewAction 33 import com.android.systemui.shared.Flags.screenshotContextUrl 34 import dagger.assisted.Assisted 35 import dagger.assisted.AssistedFactory 36 import dagger.assisted.AssistedInject 37 import java.util.UUID 38 import kotlinx.coroutines.CoroutineScope 39 import kotlinx.coroutines.launch 40 41 /** 42 * Provides actions for screenshots. This class can be overridden by a vendor-specific SysUI 43 * implementation. 44 */ 45 interface ScreenshotActionsProvider { 46 fun onScrollChipReady(onClick: Runnable) 47 48 fun onScrollChipInvalidated() 49 50 fun setCompletedScreenshot(result: ScreenshotSavedResult) 51 52 /** 53 * Provide the AssistContent for the focused task if available, null if the focused task isn't 54 * known or didn't return data. 55 */ 56 fun onAssistContent(assistContent: AssistContent?) {} 57 58 interface Factory { 59 fun create( 60 requestId: UUID, 61 request: ScreenshotData, 62 actionExecutor: ActionExecutor, 63 actionsCallback: ScreenshotActionsController.ActionsCallback, 64 ): ScreenshotActionsProvider 65 } 66 } 67 68 class DefaultScreenshotActionsProvider 69 @AssistedInject 70 constructor( 71 private val context: Context, 72 private val uiEventLogger: UiEventLogger, 73 private val actionIntentCreator: ActionIntentCreator, 74 @Application private val applicationScope: CoroutineScope, 75 @Assisted val requestId: UUID, 76 @Assisted val request: ScreenshotData, 77 @Assisted val actionExecutor: ActionExecutor, 78 @Assisted val actionsCallback: ScreenshotActionsController.ActionsCallback, 79 ) : ScreenshotActionsProvider { 80 private var addedScrollChip = false 81 private var onScrollClick: Runnable? = null 82 private var pendingAction: (suspend (ScreenshotSavedResult) -> Unit)? = null 83 private var result: ScreenshotSavedResult? = null 84 private var webUri: Uri? = null 85 86 init { 87 actionsCallback.providePreviewAction( <lambda>null88 PreviewAction(context.resources.getString(R.string.screenshot_edit_description)) { 89 debugLog(LogConfig.DEBUG_ACTIONS) { "Preview tapped" } 90 uiEventLogger.log(SCREENSHOT_PREVIEW_TAPPED, 0, request.packageNameString) 91 onDeferrableActionTapped { result -> 92 actionExecutor.startSharedTransition( 93 actionIntentCreator.createEdit(result.uri), 94 result.user, 95 true, 96 ) 97 } 98 } 99 ) 100 101 actionsCallback.provideActionButton( 102 ActionButtonAppearance( 103 AppCompatResources.getDrawable(context, R.drawable.ic_screenshot_share), 104 context.resources.getString(R.string.screenshot_share_label), 105 context.resources.getString(R.string.screenshot_share_description), 106 ), 107 showDuringEntrance = true, <lambda>null108 ) { 109 debugLog(LogConfig.DEBUG_ACTIONS) { "Share tapped" } 110 uiEventLogger.log(SCREENSHOT_SHARE_TAPPED, 0, request.packageNameString) 111 onDeferrableActionTapped { result -> 112 val uri = webUri 113 val shareIntent = 114 if (screenshotContextUrl() && uri != null) { 115 actionIntentCreator.createShareWithText( 116 result.uri, 117 extraText = uri.toString(), 118 ) 119 } else { 120 actionIntentCreator.createShareWithSubject(result.uri, result.subject) 121 } 122 actionExecutor.startSharedTransition(shareIntent, result.user, false) 123 } 124 } 125 126 actionsCallback.provideActionButton( 127 ActionButtonAppearance( 128 AppCompatResources.getDrawable(context, R.drawable.ic_screenshot_edit), 129 context.resources.getString(R.string.screenshot_edit_label), 130 context.resources.getString(R.string.screenshot_edit_description), 131 ), 132 showDuringEntrance = true, <lambda>null133 ) { 134 debugLog(LogConfig.DEBUG_ACTIONS) { "Edit tapped" } 135 uiEventLogger.log(SCREENSHOT_EDIT_TAPPED, 0, request.packageNameString) 136 onDeferrableActionTapped { result -> 137 actionExecutor.startSharedTransition( 138 actionIntentCreator.createEdit(result.uri), 139 result.user, 140 true, 141 ) 142 } 143 } 144 } 145 onScrollChipReadynull146 override fun onScrollChipReady(onClick: Runnable) { 147 onScrollClick = onClick 148 if (!addedScrollChip) { 149 actionsCallback.provideActionButton( 150 ActionButtonAppearance( 151 AppCompatResources.getDrawable(context, R.drawable.ic_screenshot_scroll), 152 context.resources.getString(R.string.screenshot_scroll_label), 153 context.resources.getString(R.string.screenshot_scroll_label), 154 ), 155 showDuringEntrance = true, 156 ) { 157 onScrollClick?.run() 158 } 159 addedScrollChip = true 160 } 161 } 162 onScrollChipInvalidatednull163 override fun onScrollChipInvalidated() { 164 onScrollClick = null 165 } 166 setCompletedScreenshotnull167 override fun setCompletedScreenshot(result: ScreenshotSavedResult) { 168 if (this.result != null) { 169 Log.e(TAG, "Got a second completed screenshot for existing request!") 170 return 171 } 172 this.result = result 173 pendingAction?.also { applicationScope.launch { it.invoke(result) } } 174 } 175 onAssistContentnull176 override fun onAssistContent(assistContent: AssistContent?) { 177 webUri = assistContent?.webUri 178 } 179 onDeferrableActionTappednull180 private fun onDeferrableActionTapped(onResult: suspend (ScreenshotSavedResult) -> Unit) { 181 result?.let { applicationScope.launch { onResult.invoke(it) } } 182 ?: run { pendingAction = onResult } 183 } 184 185 @AssistedFactory 186 interface Factory : ScreenshotActionsProvider.Factory { createnull187 override fun create( 188 requestId: UUID, 189 request: ScreenshotData, 190 actionExecutor: ActionExecutor, 191 actionsCallback: ScreenshotActionsController.ActionsCallback, 192 ): DefaultScreenshotActionsProvider 193 } 194 195 companion object { 196 private const val TAG = "ScreenshotActionsPrvdr" 197 } 198 } 199