summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ReferenceScreenshotModule.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionsProvider.kt48
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsProvider.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/SmartActionsProvider.kt285
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/screenshot/DefaultScreenshotActionsProviderTest.kt40
7 files changed, 14 insertions, 386 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ReferenceScreenshotModule.java b/packages/SystemUI/src/com/android/systemui/screenshot/ReferenceScreenshotModule.java
index 6224e1bf2414..afb0280a3917 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ReferenceScreenshotModule.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ReferenceScreenshotModule.java
@@ -16,6 +16,7 @@
package com.android.systemui.screenshot;
+import dagger.Binds;
import dagger.Module;
import dagger.Provides;
@@ -29,4 +30,9 @@ public interface ReferenceScreenshotModule {
static ScreenshotNotificationSmartActionsProvider providesScrnshtNotifSmartActionsProvider() {
return new ScreenshotNotificationSmartActionsProvider();
}
+
+ /** */
+ @Binds
+ ScreenshotActionsProvider.Factory bindScreenshotActionsProviderFactory(
+ DefaultScreenshotActionsProvider.Factory defaultScreenshotActionsProviderFactory);
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionsProvider.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionsProvider.kt
index 0ccb19cd9d31..328742543184 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionsProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionsProvider.kt
@@ -28,7 +28,6 @@ import com.android.systemui.screenshot.ActionIntentCreator.createShareWithSubjec
import com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_EDIT_TAPPED
import com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_PREVIEW_TAPPED
import com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_SHARE_TAPPED
-import com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_SMART_ACTION_TAPPED
import com.android.systemui.screenshot.ui.viewmodel.ActionButtonAppearance
import com.android.systemui.screenshot.ui.viewmodel.ScreenshotViewModel
import dagger.assisted.Assisted
@@ -59,7 +58,6 @@ class DefaultScreenshotActionsProvider
constructor(
private val context: Context,
private val viewModel: ScreenshotViewModel,
- private val smartActionsProvider: SmartActionsProvider,
private val uiEventLogger: UiEventLogger,
@Assisted val request: ScreenshotData,
@Assisted val requestId: String,
@@ -115,37 +113,6 @@ constructor(
)
}
}
-
- smartActionsProvider.requestQuickShare(request, requestId) { quickShare ->
- if (!quickShare.actionIntent.isImmutable) {
- viewModel.addAction(
- ActionButtonAppearance(
- quickShare.getIcon().loadDrawable(context),
- quickShare.title,
- quickShare.title
- )
- ) {
- debugLog(LogConfig.DEBUG_ACTIONS) { "Quickshare tapped" }
- onDeferrableActionTapped { result ->
- uiEventLogger.log(
- SCREENSHOT_SMART_ACTION_TAPPED,
- 0,
- request.packageNameString
- )
- val pendingIntentWithUri =
- smartActionsProvider.wrapIntent(
- quickShare,
- result.uri,
- result.subject,
- requestId
- )
- actionExecutor.sendPendingIntent(pendingIntentWithUri)
- }
- }
- } else {
- Log.w(TAG, "Received immutable quick share pending intent; ignoring")
- }
- }
}
override fun onScrollChipReady(onClick: Runnable) {
@@ -167,21 +134,6 @@ constructor(
}
this.result = result
pendingAction?.invoke(result)
- smartActionsProvider.requestSmartActions(request, requestId, result) { smartActions ->
- smartActions.forEach {
- smartActions.forEach { action ->
- viewModel.addAction(
- ActionButtonAppearance(
- action.getIcon().loadDrawable(context),
- action.title,
- action.title,
- )
- ) {
- actionExecutor.sendPendingIntent(action.actionIntent)
- }
- }
- }
- }
}
private fun onDeferrableActionTapped(onResult: (ScreenshotSavedResult) -> Unit) {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index 13dd229d8f62..0e48bef9a770 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -176,11 +176,12 @@ public class ScreenshotController {
// These strings are used for communicating the action invoked to
// ScreenshotNotificationSmartActionsProvider.
- static final String EXTRA_ACTION_TYPE = "android:screenshot_action_type";
- static final String EXTRA_ID = "android:screenshot_id";
- static final String EXTRA_SMART_ACTIONS_ENABLED = "android:smart_actions_enabled";
- static final String EXTRA_ACTION_INTENT = "android:screenshot_action_intent";
- static final String EXTRA_ACTION_INTENT_FILLIN = "android:screenshot_action_intent_fillin";
+ public static final String EXTRA_ACTION_TYPE = "android:screenshot_action_type";
+ public static final String EXTRA_ID = "android:screenshot_id";
+ public static final String EXTRA_SMART_ACTIONS_ENABLED = "android:smart_actions_enabled";
+ public static final String EXTRA_ACTION_INTENT = "android:screenshot_action_intent";
+ public static final String EXTRA_ACTION_INTENT_FILLIN =
+ "android:screenshot_action_intent_fillin";
// From WizardManagerHelper.java
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsProvider.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsProvider.java
index 3eafbfbf37d7..23f05e0d8337 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsProvider.java
@@ -44,7 +44,7 @@ public class ScreenshotNotificationSmartActionsProvider {
public static final String DEFAULT_ACTION_TYPE = "Smart Action";
/* Define phases of screenshot execution. */
- protected enum ScreenshotOp {
+ public enum ScreenshotOp {
OP_UNKNOWN,
RETRIEVE_SMART_ACTIONS,
REQUEST_SMART_ACTIONS,
@@ -52,7 +52,7 @@ public class ScreenshotNotificationSmartActionsProvider {
}
/* Enum to report success or failure for screenshot execution phases. */
- protected enum ScreenshotOpStatus {
+ public enum ScreenshotOpStatus {
OP_STATUS_UNKNOWN,
SUCCESS,
ERROR,
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/SmartActionsProvider.kt b/packages/SystemUI/src/com/android/systemui/screenshot/SmartActionsProvider.kt
deleted file mode 100644
index a895b300b900..000000000000
--- a/packages/SystemUI/src/com/android/systemui/screenshot/SmartActionsProvider.kt
+++ /dev/null
@@ -1,285 +0,0 @@
-/*
- * Copyright (C) 2024 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.app.Notification
-import android.app.PendingIntent
-import android.content.ClipData
-import android.content.ClipDescription
-import android.content.ComponentName
-import android.content.Context
-import android.content.Intent
-import android.graphics.Bitmap
-import android.net.Uri
-import android.os.Bundle
-import android.os.Process
-import android.os.SystemClock
-import android.os.UserHandle
-import android.provider.DeviceConfig
-import android.util.Log
-import com.android.internal.config.sysui.SystemUiDeviceConfigFlags
-import com.android.systemui.log.DebugLogger.debugLog
-import com.android.systemui.screenshot.LogConfig.DEBUG_ACTIONS
-import com.android.systemui.screenshot.ScreenshotNotificationSmartActionsProvider.ScreenshotSmartActionType.QUICK_SHARE_ACTION
-import com.android.systemui.screenshot.ScreenshotNotificationSmartActionsProvider.ScreenshotSmartActionType.REGULAR_SMART_ACTIONS
-import java.util.concurrent.CompletableFuture
-import java.util.concurrent.TimeUnit
-import java.util.concurrent.TimeoutException
-import javax.inject.Inject
-import kotlin.random.Random
-
-/**
- * Handle requesting smart/quickshare actions from the provider and executing an action when the
- * action futures complete.
- */
-class SmartActionsProvider
-@Inject
-constructor(
- private val context: Context,
- private val smartActions: ScreenshotNotificationSmartActionsProvider,
-) {
- /**
- * Requests quick share action for a given screenshot.
- *
- * @param data the ScreenshotData request
- * @param id the request id for the screenshot
- * @param onAction callback to run when quick share action is returned
- */
- fun requestQuickShare(
- data: ScreenshotData,
- id: String,
- onAction: (Notification.Action) -> Unit
- ) {
- val bitmap = data.bitmap ?: return
- val component = data.topComponent ?: ComponentName("", "")
- requestQuickShareAction(id, bitmap, component, data.getUserOrDefault()) { quickShare ->
- onAction(quickShare)
- }
- }
-
- /**
- * Requests smart actions for a given screenshot.
- *
- * @param data the ScreenshotData request
- * @param id the request id for the screenshot
- * @param result the data for the saved image
- * @param onActions callback to run when actions are returned
- */
- fun requestSmartActions(
- data: ScreenshotData,
- id: String,
- result: ScreenshotSavedResult,
- onActions: (List<Notification.Action>) -> Unit
- ) {
- val bitmap = data.bitmap ?: return
- val component = data.topComponent ?: ComponentName("", "")
- requestSmartActions(
- id,
- bitmap,
- component,
- data.getUserOrDefault(),
- result.uri,
- REGULAR_SMART_ACTIONS
- ) { actions ->
- onActions(actions)
- }
- }
-
- /**
- * Wraps the given quick share action in a broadcast intent.
- *
- * @param quickShare the quick share action to wrap
- * @param uri the URI of the saved screenshot
- * @param subject the subject/title for the screenshot
- * @param id the request ID of the screenshot
- * @return the pending intent with correct URI
- */
- fun wrapIntent(
- quickShare: Notification.Action,
- uri: Uri,
- subject: String,
- id: String
- ): PendingIntent {
- val wrappedIntent: Intent =
- Intent(context, SmartActionsReceiver::class.java)
- .putExtra(ScreenshotController.EXTRA_ACTION_INTENT, quickShare.actionIntent)
- .putExtra(
- ScreenshotController.EXTRA_ACTION_INTENT_FILLIN,
- createFillInIntent(uri, subject)
- )
- .addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
- val extras: Bundle = quickShare.extras
- val actionType =
- extras.getString(
- ScreenshotNotificationSmartActionsProvider.ACTION_TYPE,
- ScreenshotNotificationSmartActionsProvider.DEFAULT_ACTION_TYPE
- )
- // We only query for quick share actions when smart actions are enabled, so we can assert
- // that it's true here.
- wrappedIntent
- .putExtra(ScreenshotController.EXTRA_ACTION_TYPE, actionType)
- .putExtra(ScreenshotController.EXTRA_ID, id)
- .putExtra(ScreenshotController.EXTRA_SMART_ACTIONS_ENABLED, true)
- return PendingIntent.getBroadcast(
- context,
- Random.nextInt(),
- wrappedIntent,
- PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_IMMUTABLE
- )
- }
-
- private fun createFillInIntent(uri: Uri, subject: String): Intent {
- val fillIn = Intent()
- fillIn.setType("image/png")
- fillIn.putExtra(Intent.EXTRA_STREAM, uri)
- fillIn.putExtra(Intent.EXTRA_SUBJECT, subject)
- // Include URI in ClipData also, so that grantPermission picks it up.
- // We don't use setData here because some apps interpret this as "to:".
- val clipData =
- ClipData(ClipDescription("content", arrayOf("image/png")), ClipData.Item(uri))
- fillIn.clipData = clipData
- fillIn.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
- return fillIn
- }
-
- private fun requestQuickShareAction(
- id: String,
- image: Bitmap,
- component: ComponentName,
- user: UserHandle,
- timeoutMs: Long = 500,
- onAction: (Notification.Action) -> Unit
- ) {
- requestSmartActions(id, image, component, user, null, QUICK_SHARE_ACTION, timeoutMs) {
- it.firstOrNull()?.let { action -> onAction(action) }
- }
- }
-
- private fun requestSmartActions(
- id: String,
- image: Bitmap,
- component: ComponentName,
- user: UserHandle,
- uri: Uri?,
- actionType: ScreenshotNotificationSmartActionsProvider.ScreenshotSmartActionType,
- timeoutMs: Long = 500,
- onActions: (List<Notification.Action>) -> Unit
- ) {
- val enabled = isSmartActionsEnabled(user)
- debugLog(DEBUG_ACTIONS) {
- ("getSmartActionsFuture id=$id, uri=$uri, provider=$smartActions, " +
- "actionType=$actionType, smartActionsEnabled=$enabled, userHandle=$user")
- }
- if (!enabled) {
- debugLog(DEBUG_ACTIONS) { "Screenshot Intelligence not enabled, returning empty list" }
- onActions(listOf())
- return
- }
- if (image.config != Bitmap.Config.HARDWARE) {
- debugLog(DEBUG_ACTIONS) {
- "Bitmap expected: Hardware, Bitmap found: ${image.config}. Returning empty list."
- }
- onActions(listOf())
- return
- }
- val smartActionsFuture: CompletableFuture<List<Notification.Action>>
- val startTimeMs = SystemClock.uptimeMillis()
- try {
- smartActionsFuture =
- smartActions.getActions(id, uri, image, component, actionType, user)
- } catch (e: Throwable) {
- val waitTimeMs = SystemClock.uptimeMillis() - startTimeMs
- debugLog(DEBUG_ACTIONS, error = e) {
- "Failed to get future for screenshot notification smart actions."
- }
- notifyScreenshotOp(
- id,
- ScreenshotNotificationSmartActionsProvider.ScreenshotOp.REQUEST_SMART_ACTIONS,
- ScreenshotNotificationSmartActionsProvider.ScreenshotOpStatus.ERROR,
- waitTimeMs
- )
- onActions(listOf())
- return
- }
- try {
- val actions = smartActionsFuture.get(timeoutMs, TimeUnit.MILLISECONDS)
- val waitTimeMs = SystemClock.uptimeMillis() - startTimeMs
- debugLog(DEBUG_ACTIONS) {
- ("Got ${actions.size} smart actions. Wait time: $waitTimeMs ms, " +
- "actionType=$actionType")
- }
- notifyScreenshotOp(
- id,
- ScreenshotNotificationSmartActionsProvider.ScreenshotOp.WAIT_FOR_SMART_ACTIONS,
- ScreenshotNotificationSmartActionsProvider.ScreenshotOpStatus.SUCCESS,
- waitTimeMs
- )
- onActions(actions)
- } catch (e: Throwable) {
- val waitTimeMs = SystemClock.uptimeMillis() - startTimeMs
- debugLog(DEBUG_ACTIONS, error = e) {
- "Error getting smart actions. Wait time: $waitTimeMs ms, actionType=$actionType"
- }
- val status =
- if (e is TimeoutException) {
- ScreenshotNotificationSmartActionsProvider.ScreenshotOpStatus.TIMEOUT
- } else {
- ScreenshotNotificationSmartActionsProvider.ScreenshotOpStatus.ERROR
- }
- notifyScreenshotOp(
- id,
- ScreenshotNotificationSmartActionsProvider.ScreenshotOp.WAIT_FOR_SMART_ACTIONS,
- status,
- waitTimeMs
- )
- onActions(listOf())
- }
- }
-
- private fun notifyScreenshotOp(
- screenshotId: String,
- op: ScreenshotNotificationSmartActionsProvider.ScreenshotOp,
- status: ScreenshotNotificationSmartActionsProvider.ScreenshotOpStatus,
- durationMs: Long
- ) {
- debugLog(DEBUG_ACTIONS) {
- "$smartActions notifyOp: $op id=$screenshotId, status=$status, durationMs=$durationMs"
- }
- try {
- smartActions.notifyOp(screenshotId, op, status, durationMs)
- } catch (e: Throwable) {
- Log.e(TAG, "Error in notifyScreenshotOp: ", e)
- }
- }
-
- private fun isSmartActionsEnabled(user: UserHandle): Boolean {
- // Smart actions don't yet work for cross-user saves.
- val savingToOtherUser = user !== Process.myUserHandle()
- val actionsEnabled =
- DeviceConfig.getBoolean(
- DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.ENABLE_SCREENSHOT_NOTIFICATION_SMART_ACTIONS,
- true
- )
- return !savingToOtherUser && actionsEnabled
- }
-
- companion object {
- private const val TAG = "SmartActionsProvider"
- private const val SCREENSHOT_SHARE_SUBJECT_TEMPLATE = "Screenshot (%s)"
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java b/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java
index 6ff0fdaeab0f..ab23e5fdb7d9 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java
@@ -22,11 +22,9 @@ import android.app.Service;
import android.view.accessibility.AccessibilityManager;
import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.screenshot.DefaultScreenshotActionsProvider;
import com.android.systemui.screenshot.ImageCapture;
import com.android.systemui.screenshot.ImageCaptureImpl;
import com.android.systemui.screenshot.LegacyScreenshotViewProxy;
-import com.android.systemui.screenshot.ScreenshotActionsProvider;
import com.android.systemui.screenshot.ScreenshotPolicy;
import com.android.systemui.screenshot.ScreenshotPolicyImpl;
import com.android.systemui.screenshot.ScreenshotShelfViewProxy;
@@ -90,10 +88,6 @@ public abstract class ScreenshotModule {
abstract ScreenshotSoundController bindScreenshotSoundController(
ScreenshotSoundControllerImpl screenshotSoundProviderImpl);
- @Binds
- abstract ScreenshotActionsProvider.Factory bindScreenshotActionsProviderFactory(
- DefaultScreenshotActionsProvider.Factory defaultScreenshotActionsProviderFactory);
-
@Provides
@SysUISingleton
static ScreenshotViewModel providesScreenshotViewModel(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/DefaultScreenshotActionsProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/DefaultScreenshotActionsProviderTest.kt
index bde821b79469..853e50a12ea5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/DefaultScreenshotActionsProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/DefaultScreenshotActionsProviderTest.kt
@@ -16,8 +16,6 @@
package com.android.systemui.screenshot
-import android.app.Notification
-import android.app.PendingIntent
import android.content.Intent
import android.net.Uri
import android.os.Process
@@ -27,8 +25,6 @@ import android.view.accessibility.AccessibilityManager
import androidx.test.filters.SmallTest
import com.android.internal.logging.UiEventLogger
import com.android.systemui.SysuiTestCase
-import com.android.systemui.clipboardoverlay.EditTextActivity
-import com.android.systemui.res.R
import com.android.systemui.screenshot.ui.viewmodel.ScreenshotViewModel
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.capture
@@ -41,10 +37,7 @@ import org.junit.Assert.assertNotNull
import org.junit.Before
import org.junit.runner.RunWith
import org.mockito.Mockito.verifyNoMoreInteractions
-import org.mockito.kotlin.any
-import org.mockito.kotlin.never
import org.mockito.kotlin.verify
-import org.mockito.kotlin.whenever
@RunWith(AndroidTestingRunner::class)
@SmallTest
@@ -52,7 +45,6 @@ class DefaultScreenshotActionsProviderTest : SysuiTestCase() {
private val actionExecutor = mock<ActionExecutor>()
private val accessibilityManager = mock<AccessibilityManager>()
private val uiEventLogger = mock<UiEventLogger>()
- private val smartActionsProvider = mock<SmartActionsProvider>()
private val request = ScreenshotData.forTesting()
private val validResult = ScreenshotSavedResult(Uri.EMPTY, Process.myUserHandle(), 0)
@@ -119,42 +111,10 @@ class DefaultScreenshotActionsProviderTest : SysuiTestCase() {
assertThat(intentCaptor.value.action).isEqualTo(Intent.ACTION_CHOOSER)
}
- @Test
- fun quickShareTapped_wrapsAndSendsIntent() = runTest {
- val quickShare =
- Notification.Action(
- R.drawable.ic_screenshot_edit,
- "TestQuickShare",
- PendingIntent.getActivity(
- context,
- 0,
- Intent(context, EditTextActivity::class.java),
- PendingIntent.FLAG_MUTABLE
- )
- )
- whenever(smartActionsProvider.requestQuickShare(any(), any(), any())).then {
- (it.getArgument(2) as ((Notification.Action) -> Unit)).invoke(quickShare)
- }
- whenever(smartActionsProvider.wrapIntent(any(), any(), any(), any())).thenAnswer {
- (it.getArgument(0) as Notification.Action).actionIntent
- }
- actionsProvider = createActionsProvider()
-
- viewModel.actions.value[2].onClicked?.invoke()
- verify(uiEventLogger, never())
- .log(eq(ScreenshotEvent.SCREENSHOT_SMART_ACTION_TAPPED), any(), any())
- verify(smartActionsProvider, never()).wrapIntent(any(), any(), any(), any())
- actionsProvider.setCompletedScreenshot(validResult)
- verify(smartActionsProvider)
- .wrapIntent(eq(quickShare), eq(validResult.uri), eq(validResult.subject), eq("testid"))
- verify(uiEventLogger).log(eq(ScreenshotEvent.SCREENSHOT_SMART_ACTION_TAPPED), eq(0), eq(""))
- }
-
private fun createActionsProvider(): ScreenshotActionsProvider {
return DefaultScreenshotActionsProvider(
context,
viewModel,
- smartActionsProvider,
uiEventLogger,
request,
"testid",