From 94f2cf048d55c36745542000f2f3276e2d04448f Mon Sep 17 00:00:00 2001 From: Andrey Epin Date: Wed, 14 Feb 2024 15:39:46 -0800 Subject: Parse remaining payload selection callback result arguments Bug: 302691505 Test: atest IntentResolver-tests-unit Change-Id: I93ab3abb9605a32b4ce44572a027c478c3ea1210 --- .../contentpreview/PayloadToggleInteractor.kt | 21 ++++++--- .../contentpreview/SelectionChangeCallback.kt | 55 ++++++++++++++++------ .../v2/ui/viewmodel/ChooserRequestReader.kt | 22 ++++----- 3 files changed, 67 insertions(+), 31 deletions(-) (limited to 'java/src') diff --git a/java/src/com/android/intentresolver/contentpreview/PayloadToggleInteractor.kt b/java/src/com/android/intentresolver/contentpreview/PayloadToggleInteractor.kt index 003f6884..6f4f5167 100644 --- a/java/src/com/android/intentresolver/contentpreview/PayloadToggleInteractor.kt +++ b/java/src/com/android/intentresolver/contentpreview/PayloadToggleInteractor.kt @@ -17,8 +17,10 @@ package com.android.intentresolver.contentpreview import android.content.Intent +import android.content.IntentSender import android.net.Uri import android.service.chooser.ChooserAction +import android.service.chooser.ChooserTarget import android.util.Log import android.util.SparseArray import java.io.Closeable @@ -43,7 +45,7 @@ private const val TAG = "PayloadToggleInteractor" @OptIn(ExperimentalCoroutinesApi::class) class PayloadToggleInteractor( - // must use single-thread dispatcher (or we should enforce it with a lock) + // TODO: a single-thread dispatcher is currently expected. iterate on the synchronization logic. private val scope: CoroutineScope, private val initiallySharedUris: List, private val focusedUriIdx: Int, @@ -51,7 +53,7 @@ class PayloadToggleInteractor( private val cursorReaderProvider: suspend () -> CursorReader, private val uriMetadataReader: (Uri) -> FileInfo, private val targetIntentModifier: (List) -> Intent, - private val selectionCallback: (Intent) -> CallbackResult?, + private val selectionCallback: (Intent) -> ShareouselUpdate?, ) { private var cursorDataRef = CompletableDeferred() private val records = LinkedList() @@ -183,7 +185,7 @@ class PayloadToggleInteractor( val (reader, selectionTracker) = waitForCursorData() ?: return if (!reader.hasMoreBefore) return - val newItems = reader.readPageBefore().toRecords() + val newItems = reader.readPageBefore().toItems() selectionTracker.onStartItemsAdded(newItems) for (i in newItems.size() - 1 downTo 0) { records.add( @@ -224,7 +226,7 @@ class PayloadToggleInteractor( val (reader, selectionTracker) = waitForCursorData() ?: return if (!reader.hasMoreAfter) return - val newItems = reader.readPageAfter().toRecords() + val newItems = reader.readPageAfter().toItems() selectionTracker.onEndItemsAdded(newItems) for (i in 0 until newItems.size()) { val key = newItems.keyAt(i) @@ -254,7 +256,7 @@ class PayloadToggleInteractor( } } - private fun SparseArray.toRecords(): SparseArray { + private fun SparseArray.toItems(): SparseArray { val items = SparseArray(size()) for (i in 0 until size()) { val key = keyAt(i) @@ -335,7 +337,14 @@ class PayloadToggleInteractor( val isSelected = MutableStateFlow(false) } - data class CallbackResult(val customActions: List?) + data class ShareouselUpdate( + // for all properties, null value means no change + val customActions: List? = null, + val modifyShareAction: ChooserAction? = null, + val alternateIntents: List? = null, + val callerTargets: List? = null, + val refinementIntentSender: IntentSender? = null, + ) private data class CursorData( val reader: CursorReader, diff --git a/java/src/com/android/intentresolver/contentpreview/SelectionChangeCallback.kt b/java/src/com/android/intentresolver/contentpreview/SelectionChangeCallback.kt index 4e2e37b8..5c916882 100644 --- a/java/src/com/android/intentresolver/contentpreview/SelectionChangeCallback.kt +++ b/java/src/com/android/intentresolver/contentpreview/SelectionChangeCallback.kt @@ -18,13 +18,25 @@ package com.android.intentresolver.contentpreview import android.content.ContentInterface import android.content.Intent -import android.content.Intent.EXTRA_CHOOSER_CUSTOM_ACTIONS +import android.content.Intent.EXTRA_CHOOSER_MODIFY_SHARE_ACTION +import android.content.Intent.EXTRA_CHOOSER_REFINEMENT_INTENT_SENDER +import android.content.Intent.EXTRA_CHOOSER_TARGETS import android.content.Intent.EXTRA_INTENT +import android.content.IntentSender import android.net.Uri import android.os.Bundle import android.service.chooser.AdditionalContentContract.MethodNames.ON_SELECTION_CHANGED import android.service.chooser.ChooserAction -import com.android.intentresolver.contentpreview.PayloadToggleInteractor.CallbackResult +import android.service.chooser.ChooserTarget +import com.android.intentresolver.contentpreview.PayloadToggleInteractor.ShareouselUpdate +import com.android.intentresolver.v2.ui.viewmodel.readAlternateIntents +import com.android.intentresolver.v2.ui.viewmodel.readChooserActions +import com.android.intentresolver.v2.validation.ValidationResult +import com.android.intentresolver.v2.validation.types.array +import com.android.intentresolver.v2.validation.types.value +import com.android.intentresolver.v2.validation.validateFrom + +private const val TAG = "SelectionChangeCallback" /** * Encapsulates payload change callback invocation to the sharing app; handles callback arguments @@ -34,8 +46,8 @@ class SelectionChangeCallback( private val uri: Uri, private val chooserIntent: Intent, private val contentResolver: ContentInterface, -) : (Intent) -> CallbackResult? { - fun onSelectionChanged(targetIntent: Intent): CallbackResult? = +) : (Intent) -> ShareouselUpdate? { + fun onSelectionChanged(targetIntent: Intent): ShareouselUpdate? = contentResolver .call( requireNotNull(uri.authority) { "URI authority can not be null" }, @@ -49,20 +61,35 @@ class SelectionChangeCallback( } ) ?.let { bundle -> - val actions = - if (bundle.containsKey(EXTRA_CHOOSER_CUSTOM_ACTIONS)) { - bundle - .getParcelableArray( - EXTRA_CHOOSER_CUSTOM_ACTIONS, - ChooserAction::class.java - ) - ?.filterNotNull() - ?: emptyList() + readCallbackResponse(bundle).let { validation -> + if (validation.isSuccess()) { + validation.value } else { + validation.reportToLogcat(TAG) null } - CallbackResult(actions) + } } override fun invoke(targetIntent: Intent) = onSelectionChanged(targetIntent) + + private fun readCallbackResponse(bundle: Bundle): ValidationResult { + return validateFrom(bundle::get) { + val customActions = readChooserActions() + val modifyShareAction = + optional(value(EXTRA_CHOOSER_MODIFY_SHARE_ACTION)) + val alternateIntents = readAlternateIntents() + val callerTargets = optional(array(EXTRA_CHOOSER_TARGETS)) + val refinementIntentSender = + optional(value(EXTRA_CHOOSER_REFINEMENT_INTENT_SENDER)) + + ShareouselUpdate( + customActions, + modifyShareAction, + alternateIntents, + callerTargets, + refinementIntentSender, + ) + } + } } diff --git a/java/src/com/android/intentresolver/v2/ui/viewmodel/ChooserRequestReader.kt b/java/src/com/android/intentresolver/v2/ui/viewmodel/ChooserRequestReader.kt index e32d69b0..f8ab6fcb 100644 --- a/java/src/com/android/intentresolver/v2/ui/viewmodel/ChooserRequestReader.kt +++ b/java/src/com/android/intentresolver/v2/ui/viewmodel/ChooserRequestReader.kt @@ -28,7 +28,6 @@ import android.content.Intent.EXTRA_EXCLUDE_COMPONENTS import android.content.Intent.EXTRA_INITIAL_INTENTS import android.content.Intent.EXTRA_INTENT import android.content.Intent.EXTRA_METADATA_TEXT -import android.content.Intent.EXTRA_REFERRER import android.content.Intent.EXTRA_REPLACEMENT_EXTRAS import android.content.Intent.EXTRA_TEXT import android.content.Intent.EXTRA_TITLE @@ -49,6 +48,7 @@ import com.android.intentresolver.v2.ext.hasSendAction import com.android.intentresolver.v2.ext.ifMatch import com.android.intentresolver.v2.ui.model.ActivityLaunch import com.android.intentresolver.v2.ui.model.ChooserRequest +import com.android.intentresolver.v2.validation.Validation import com.android.intentresolver.v2.validation.ValidationResult import com.android.intentresolver.v2.validation.types.IntentOrUri import com.android.intentresolver.v2.validation.types.array @@ -75,9 +75,7 @@ fun readChooserRequest( val isSendAction = targetIntent.hasSendAction() - val additionalTargets = - optional(array(EXTRA_ALTERNATE_INTENTS))?.map { it.maybeAddSendActionFlags() } - ?: emptyList() + val additionalTargets = readAlternateIntents() ?: emptyList() val replacementExtras = optional(value(EXTRA_REPLACEMENT_EXTRAS)) @@ -119,16 +117,10 @@ fun readChooserRequest( val sharedText = optional(value(EXTRA_TEXT)) - val chooserActions = - optional(array(EXTRA_CHOOSER_CUSTOM_ACTIONS)) - ?.filter { hasValidIcon(it) } - ?.take(MAX_CHOOSER_ACTIONS) - ?: emptyList() + val chooserActions = readChooserActions() ?: emptyList() val modifyShareAction = optional(value(EXTRA_CHOOSER_MODIFY_SHARE_ACTION)) - val referrerFillIn = Intent().putExtra(EXTRA_REFERRER, launch.referrer) - val additionalContentUri: Uri? val focusedItemPos: Int if (isSendAction && flags.chooserPayloadToggling()) { @@ -188,6 +180,14 @@ fun readChooserRequest( } } +fun Validation.readAlternateIntents(): List? = + optional(array(EXTRA_ALTERNATE_INTENTS))?.map { it.maybeAddSendActionFlags() } + +fun Validation.readChooserActions(): List? = + optional(array(EXTRA_CHOOSER_CUSTOM_ACTIONS)) + ?.filter { hasValidIcon(it) } + ?.take(MAX_CHOOSER_ACTIONS) + private fun Intent.toShareTargetFilter(): IntentFilter? { return type?.let { IntentFilter().apply { -- cgit v1.2.3-59-g8ed1b