Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 431c5649 authored by Miranda Kephart's avatar Miranda Kephart Committed by Matt Casey
Browse files

Create ScreenshotResult class to simplify null checks

Bug: 329659738
Test: atest DefaultScreenshotActionsProviderTest
Flag: ACONFIG com.android.systemui.screenshot_shelf_ui DEVELOPMENT
Change-Id: I0fced77866375aa3d9fc447343884c96a9c6a936
Merged-In: I0fced77866375aa3d9fc447343884c96a9c6a936
parent f1e70448
Loading
Loading
Loading
Loading
+48 −88
Original line number Diff line number Diff line
@@ -23,21 +23,17 @@ import android.app.PendingIntent
import android.app.assist.AssistContent
import android.content.Context
import android.content.Intent
import android.os.Process
import android.os.UserHandle
import android.provider.DeviceConfig
import android.util.Log
import android.util.Pair
import androidx.appcompat.content.res.AppCompatResources
import com.android.app.tracing.coroutines.launch
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags
import com.android.internal.logging.UiEventLogger
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.log.DebugLogger.debugLog
import com.android.systemui.res.R
import com.android.systemui.screenshot.ActionIntentCreator.createEdit
import com.android.systemui.screenshot.ActionIntentCreator.createShareWithSubject
import com.android.systemui.screenshot.ScreenshotController.SavedImageData
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
@@ -47,8 +43,6 @@ import com.android.systemui.screenshot.ui.viewmodel.ScreenshotViewModel
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import java.text.DateFormat
import java.util.Date
import kotlinx.coroutines.CoroutineScope

/**
@@ -56,7 +50,7 @@ import kotlinx.coroutines.CoroutineScope
 * implementation.
 */
interface ScreenshotActionsProvider {
    fun setCompletedScreenshot(result: SavedImageData)
    fun setCompletedScreenshot(result: ScreenshotSavedResult)
    fun isPendingSharedTransition(): Boolean

    fun onAssistContentAvailable(assistContent: AssistContent) {}
@@ -85,8 +79,8 @@ constructor(
    @Assisted val windowTransition: () -> Pair<ActivityOptions, ExitTransitionCoordinator>,
    @Assisted val requestDismissal: () -> Unit,
) : ScreenshotActionsProvider {
    private var pendingAction: ((SavedImageData) -> Unit)? = null
    private var result: SavedImageData? = null
    private var pendingAction: ((ScreenshotSavedResult) -> Unit)? = null
    private var result: ScreenshotSavedResult? = null
    private var isPendingSharedTransition = false

    init {
@@ -94,7 +88,7 @@ constructor(
            debugLog(LogConfig.DEBUG_ACTIONS) { "Preview tapped" }
            uiEventLogger.log(SCREENSHOT_PREVIEW_TAPPED, 0, request.packageNameString)
            onDeferrableActionTapped { result ->
                startSharedTransition(createEdit(result.uri, context), true)
                startSharedTransition(createEdit(result.uri, context), result.user, true)
            }
        }
        viewModel.addAction(
@@ -106,7 +100,7 @@ constructor(
                debugLog(LogConfig.DEBUG_ACTIONS) { "Edit tapped" }
                uiEventLogger.log(SCREENSHOT_EDIT_TAPPED, 0, request.packageNameString)
                onDeferrableActionTapped { result ->
                    startSharedTransition(createEdit(result.uri, context), true)
                    startSharedTransition(createEdit(result.uri, context), result.user, true)
                }
            }
        )
@@ -119,18 +113,21 @@ constructor(
                debugLog(LogConfig.DEBUG_ACTIONS) { "Share tapped" }
                uiEventLogger.log(SCREENSHOT_SHARE_TAPPED, 0, request.packageNameString)
                onDeferrableActionTapped { result ->
                    startSharedTransition(createShareWithSubject(result.uri, result.subject), false)
                    startSharedTransition(
                        createShareWithSubject(result.uri, result.subject),
                        result.user,
                        false
                    )
                }
            }
        )
        if (smartActionsEnabled(request.userHandle ?: Process.myUserHandle())) {
        smartActionsProvider.requestQuickShare(request, requestId) { quickShare ->
            if (!quickShare.actionIntent.isImmutable) {
                viewModel.addAction(
                    ActionButtonViewModel(
                        quickShare.getIcon().loadDrawable(context),
                        quickShare.title,
                            quickShare.title,
                        quickShare.title
                    ) {
                        debugLog(LogConfig.DEBUG_ACTIONS) { "Quickshare tapped" }
                        onDeferrableActionTapped { result ->
@@ -141,12 +138,7 @@ constructor(
                            )
                            sendPendingIntent(
                                smartActionsProvider
                                        .wrapIntent(
                                            quickShare,
                                            result.uri,
                                            result.subject,
                                            requestId
                                        )
                                    .wrapIntent(quickShare, result.uri, result.subject, requestId)
                                    .actionIntent
                            )
                        }
@@ -157,54 +149,38 @@ constructor(
            }
        }
    }
    }

    override fun setCompletedScreenshot(result: SavedImageData) {
    override fun setCompletedScreenshot(result: ScreenshotSavedResult) {
        if (this.result != null) {
            Log.e(TAG, "Got a second completed screenshot for existing request!")
            return
        }
        if (result.uri == null || result.owner == null || result.imageTime == null) {
            Log.e(TAG, "Invalid result provided!")
            return
        }
        if (result.subject == null) {
            result.subject = getSubjectString(result.imageTime)
        }
        this.result = result
        pendingAction?.invoke(result)
        if (smartActionsEnabled(result.owner)) {
        smartActionsProvider.requestSmartActions(request, requestId, result) { smartActions ->
            viewModel.addActions(
                smartActions.map {
                        ActionButtonViewModel(
                            it.getIcon().loadDrawable(context),
                            it.title,
                            it.title,
                        ) {
                    ActionButtonViewModel(it.getIcon().loadDrawable(context), it.title, it.title) {
                        sendPendingIntent(it.actionIntent)
                    }
                }
            )
        }
    }
    }

    override fun isPendingSharedTransition(): Boolean {
        return isPendingSharedTransition
    }

    private fun onDeferrableActionTapped(onResult: (SavedImageData) -> Unit) {
    private fun onDeferrableActionTapped(onResult: (ScreenshotSavedResult) -> Unit) {
        result?.let { onResult.invoke(it) } ?: run { pendingAction = onResult }
    }

    private fun startSharedTransition(intent: Intent, overrideTransition: Boolean) {
        val user =
            result?.owner
                ?: run {
                    Log.wtf(TAG, "User handle not provided in screenshot result! Result: $result")
                    return
                }
    private fun startSharedTransition(
        intent: Intent,
        user: UserHandle,
        overrideTransition: Boolean
    ) {
        isPendingSharedTransition = true
        applicationScope.launch("$TAG#launchIntentAsync") {
            actionExecutor.launchIntent(intent, windowTransition.invoke(), user, overrideTransition)
@@ -225,21 +201,6 @@ constructor(
        }
    }

    private fun smartActionsEnabled(user: UserHandle): Boolean {
        val savingToOtherUser = user != Process.myUserHandle()
        return !savingToOtherUser &&
            DeviceConfig.getBoolean(
                DeviceConfig.NAMESPACE_SYSTEMUI,
                SystemUiDeviceConfigFlags.ENABLE_SCREENSHOT_NOTIFICATION_SMART_ACTIONS,
                true
            )
    }

    private fun getSubjectString(imageTime: Long): String {
        val subjectDate = DateFormat.getDateTimeInstance().format(Date(imageTime))
        return String.format(SCREENSHOT_SHARE_SUBJECT_TEMPLATE, subjectDate)
    }

    @AssistedFactory
    interface Factory : ScreenshotActionsProvider.Factory {
        override fun create(
@@ -252,6 +213,5 @@ constructor(

    companion object {
        private const val TAG = "ScreenshotActionsProvider"
        private const val SCREENSHOT_SHARE_SUBJECT_TEMPLATE = "Screenshot (%s)"
    }
}
+3 −7
Original line number Diff line number Diff line
@@ -942,7 +942,7 @@ public class ScreenshotController {
    private void saveScreenshotInBackground(
            ScreenshotData screenshot, UUID requestId, Consumer<Uri> finisher) {
        ListenableFuture<ImageExporter.Result> future = mImageExporter.export(mBgExecutor,
                requestId, screenshot.getBitmap(), screenshot.getUserHandle(), mDisplayId);
                requestId, screenshot.getBitmap(), screenshot.getUserOrDefault(), mDisplayId);
        future.addListener(() -> {
            try {
                ImageExporter.Result result = future.get();
@@ -950,12 +950,8 @@ public class ScreenshotController {
                logScreenshotResultStatus(result.uri, screenshot.getUserHandle());
                mScreenshotHandler.resetTimeout();
                if (result.uri != null) {
                    final SavedImageData savedImageData = new SavedImageData();
                    savedImageData.uri = result.uri;
                    savedImageData.owner = screenshot.getUserHandle();
                    savedImageData.imageTime = result.timestamp;
                    mActionsProvider.setCompletedScreenshot(savedImageData);
                    mViewProxy.setChipIntents(savedImageData);
                    mActionsProvider.setCompletedScreenshot(new ScreenshotSavedResult(
                            result.uri, screenshot.getUserOrDefault(), result.timestamp));
                }
                if (DEBUG_CALLBACK) {
                    Log.d(TAG, "finished background processing, Calling (Consumer<Uri>) "
+5 −0
Original line number Diff line number Diff line
@@ -5,6 +5,7 @@ import android.graphics.Bitmap
import android.graphics.Insets
import android.graphics.Rect
import android.net.Uri
import android.os.Process
import android.os.UserHandle
import android.view.Display
import android.view.WindowManager.ScreenshotSource
@@ -31,6 +32,10 @@ data class ScreenshotData(
    val packageNameString: String
        get() = if (topComponent == null) "" else topComponent!!.packageName

    fun getUserOrDefault(): UserHandle {
        return userHandle ?: Process.myUserHandle()
    }

    companion object {
        @JvmStatic
        fun fromRequest(request: ScreenshotRequest, displayId: Int = Display.DEFAULT_DISPLAY) =
+39 −0
Original line number Diff line number Diff line
/*
 * 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.net.Uri
import android.os.UserHandle
import java.text.DateFormat
import java.util.Date

/**
 * Represents a saved screenshot, with the uri and user it was saved to as well as the time it was
 * saved.
 */
data class ScreenshotSavedResult(val uri: Uri, val user: UserHandle, val imageTime: Long) {
    val subject: String

    init {
        val subjectDate = DateFormat.getDateTimeInstance().format(Date(imageTime))
        subject = String.format(SCREENSHOT_SHARE_SUBJECT_TEMPLATE, subjectDate)
    }

    companion object {
        private const val SCREENSHOT_SHARE_SUBJECT_TEMPLATE = "Screenshot (%s)"
    }
}
+13 −8
Original line number Diff line number Diff line
@@ -65,10 +65,9 @@ constructor(
        onAction: (Notification.Action) -> Unit
    ) {
        val bitmap = data.bitmap ?: return
        val user = data.userHandle ?: return
        val component = data.topComponent ?: ComponentName("", "")
        requestQuickShareAction(id, bitmap, component, user) { quickShareAction ->
            onAction(quickShareAction)
        requestQuickShareAction(id, bitmap, component, data.getUserOrDefault()) { quickShare ->
            onAction(quickShare)
        }
    }

@@ -83,14 +82,19 @@ constructor(
    fun requestSmartActions(
        data: ScreenshotData,
        id: String,
        result: ScreenshotController.SavedImageData,
        result: ScreenshotSavedResult,
        onActions: (List<Notification.Action>) -> Unit
    ) {
        val bitmap = data.bitmap ?: return
        val user = data.userHandle ?: return
        val uri = result.uri ?: return
        val component = data.topComponent ?: ComponentName("", "")
        requestSmartActions(id, bitmap, component, user, uri, REGULAR_SMART_ACTIONS) { actions ->
        requestSmartActions(
            id,
            bitmap,
            component,
            data.getUserOrDefault(),
            result.uri,
            REGULAR_SMART_ACTIONS
        ) { actions ->
            onActions(actions)
        }
    }
@@ -197,7 +201,7 @@ constructor(
            onActions(listOf())
            return
        }
        var smartActionsFuture: CompletableFuture<List<Notification.Action>>
        val smartActionsFuture: CompletableFuture<List<Notification.Action>>
        val startTimeMs = SystemClock.uptimeMillis()
        try {
            smartActionsFuture =
@@ -266,6 +270,7 @@ constructor(
            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()
Loading