From e3d2e850f9caeb4df9389ef40d17a650b7bb505a Mon Sep 17 00:00:00 2001 From: Andrey Epin Date: Tue, 12 Mar 2024 17:50:15 -0700 Subject: Update remaining payload change callback values Moves ShareouselUpdate out of SelectionChangeCallback into the model package. Receives result intent sender and metadata text from the callback. Propagate chooser requst changes from the callback to the ChooserViewModel; update result sender in ChooserActivity. Bug: 302691505 Test: IntentResolver-tests-unit Test: manual test with the updated CTS-V test Change-Id: I3b05cb35b94cb820c5f4fdc9fbc336dd072c36bd --- .../repository/ChooserParamsUpdateRepository.kt | 34 +++++++++++++ .../interactor/UpdateTargetIntentInteractor.kt | 12 +++-- .../payloadtoggle/domain/model/ShareouselUpdate.kt | 34 +++++++++++++ .../domain/update/SelectionChangeCallback.kt | 31 +++++++----- .../android/intentresolver/v2/ChooserActivity.java | 34 ++++++++++--- .../interactor/ChooserRequestUpdateInteractor.kt | 58 ++++++++++++++++++++-- 6 files changed, 176 insertions(+), 27 deletions(-) create mode 100644 java/src/com/android/intentresolver/contentpreview/payloadtoggle/data/repository/ChooserParamsUpdateRepository.kt create mode 100644 java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/model/ShareouselUpdate.kt (limited to 'java/src') diff --git a/java/src/com/android/intentresolver/contentpreview/payloadtoggle/data/repository/ChooserParamsUpdateRepository.kt b/java/src/com/android/intentresolver/contentpreview/payloadtoggle/data/repository/ChooserParamsUpdateRepository.kt new file mode 100644 index 00000000..1a4f2b83 --- /dev/null +++ b/java/src/com/android/intentresolver/contentpreview/payloadtoggle/data/repository/ChooserParamsUpdateRepository.kt @@ -0,0 +1,34 @@ +/* + * Copyright 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.intentresolver.contentpreview.payloadtoggle.data.repository + +import com.android.intentresolver.contentpreview.payloadtoggle.domain.model.ShareouselUpdate +import dagger.hilt.android.scopes.ViewModelScoped +import javax.inject.Inject +import kotlinx.coroutines.flow.MutableStateFlow + +/** Chooser parameters Updates received from the sharing application payload change callback */ +// TODO: a scaffolding repository to deliver chooser parameter updates before we developed some +// other, more thought-through solution. +@ViewModelScoped +class ChooserParamsUpdateRepository @Inject constructor() { + val updates = MutableStateFlow(null) + + fun setUpdates(update: ShareouselUpdate) { + updates.tryEmit(update) + } +} diff --git a/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/UpdateTargetIntentInteractor.kt b/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/UpdateTargetIntentInteractor.kt index 4619e478..4cb1f5b6 100644 --- a/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/UpdateTargetIntentInteractor.kt +++ b/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/UpdateTargetIntentInteractor.kt @@ -18,6 +18,7 @@ package com.android.intentresolver.contentpreview.payloadtoggle.domain.interactor +import com.android.intentresolver.contentpreview.payloadtoggle.data.repository.ChooserParamsUpdateRepository import com.android.intentresolver.contentpreview.payloadtoggle.data.repository.PreviewSelectionsRepository import com.android.intentresolver.contentpreview.payloadtoggle.data.repository.TargetIntentRepository import com.android.intentresolver.contentpreview.payloadtoggle.domain.intent.CustomAction @@ -30,6 +31,7 @@ import javax.inject.Inject import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.mapLatest import kotlinx.coroutines.launch @@ -38,6 +40,7 @@ class UpdateTargetIntentInteractor @Inject constructor( private val intentRepository: TargetIntentRepository, + private val chooserParamsUpdateRepository: ChooserParamsUpdateRepository, @CustomAction private val pendingIntentSender: PendingIntentSender, private val selectionCallback: SelectionChangeCallback, private val selectionRepo: PreviewSelectionsRepository, @@ -47,12 +50,13 @@ constructor( suspend fun launch(): Unit = coroutineScope { launch { intentRepository.targetIntent - .mapLatest { targetIntent -> - selectionCallback.onSelectionChanged(targetIntent)?.customActions ?: emptyList() - } - .collect { actions -> + .mapLatest { targetIntent -> selectionCallback.onSelectionChanged(targetIntent) } + .filterNotNull() + .collect { updates -> + val actions = updates.customActions ?: emptyList() intentRepository.customActions.value = actions.map { it.toCustomActionModel(pendingIntentSender) } + chooserParamsUpdateRepository.setUpdates(updates) } } launch { diff --git a/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/model/ShareouselUpdate.kt b/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/model/ShareouselUpdate.kt new file mode 100644 index 00000000..41a34d1a --- /dev/null +++ b/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/model/ShareouselUpdate.kt @@ -0,0 +1,34 @@ +/* + * Copyright 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.intentresolver.contentpreview.payloadtoggle.domain.model + +import android.content.Intent +import android.content.IntentSender +import android.service.chooser.ChooserAction +import android.service.chooser.ChooserTarget + +/** Sharing session updates provided by the sharing app from the payload change callback */ +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, + val resultIntentSender: IntentSender? = null, + val metadataText: CharSequence? = null, +) diff --git a/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/update/SelectionChangeCallback.kt b/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/update/SelectionChangeCallback.kt index 03295a31..e7644dc5 100644 --- a/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/update/SelectionChangeCallback.kt +++ b/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/update/SelectionChangeCallback.kt @@ -20,17 +20,20 @@ import android.content.ContentInterface import android.content.Intent import android.content.Intent.EXTRA_CHOOSER_MODIFY_SHARE_ACTION import android.content.Intent.EXTRA_CHOOSER_REFINEMENT_INTENT_SENDER +import android.content.Intent.EXTRA_CHOOSER_RESULT_INTENT_SENDER import android.content.Intent.EXTRA_CHOOSER_TARGETS import android.content.Intent.EXTRA_INTENT +import android.content.Intent.EXTRA_METADATA_TEXT 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 android.service.chooser.ChooserTarget -import com.android.intentresolver.contentpreview.payloadtoggle.domain.update.SelectionChangeCallback.ShareouselUpdate +import com.android.intentresolver.contentpreview.payloadtoggle.domain.model.ShareouselUpdate import com.android.intentresolver.inject.AdditionalContent import com.android.intentresolver.inject.ChooserIntent +import com.android.intentresolver.inject.ChooserServiceFlags import com.android.intentresolver.v2.ui.viewmodel.readAlternateIntents import com.android.intentresolver.v2.ui.viewmodel.readChooserActions import com.android.intentresolver.v2.validation.Invalid @@ -56,15 +59,6 @@ private const val TAG = "SelectionChangeCallback" */ fun interface SelectionChangeCallback { suspend fun onSelectionChanged(targetIntent: Intent): ShareouselUpdate? - - 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, - ) } class SelectionChangeCallbackImpl @@ -73,6 +67,7 @@ constructor( @AdditionalContent private val uri: Uri, @ChooserIntent private val chooserIntent: Intent, private val contentResolver: ContentInterface, + private val flags: ChooserServiceFlags, ) : SelectionChangeCallback { private val mutex = Mutex() @@ -92,7 +87,7 @@ constructor( ) } ?.let { bundle -> - return when (val result = readCallbackResponse(bundle)) { + return when (val result = readCallbackResponse(bundle, flags)) { is Valid -> result.value is Invalid -> { result.errors.forEach { it.log(TAG) } @@ -102,7 +97,10 @@ constructor( } } -private fun readCallbackResponse(bundle: Bundle): ValidationResult { +private fun readCallbackResponse( + bundle: Bundle, + flags: ChooserServiceFlags +): ValidationResult { return validateFrom(bundle::get) { val customActions = readChooserActions() val modifyShareAction = optional(value(EXTRA_CHOOSER_MODIFY_SHARE_ACTION)) @@ -110,6 +108,13 @@ private fun readCallbackResponse(bundle: Bundle): ValidationResult(EXTRA_CHOOSER_TARGETS)) val refinementIntentSender = optional(value(EXTRA_CHOOSER_REFINEMENT_INTENT_SENDER)) + val resultIntentSender = optional(value(EXTRA_CHOOSER_RESULT_INTENT_SENDER)) + val metadataText = + if (flags.enableSharesheetMetadataExtra()) { + optional(value(EXTRA_METADATA_TEXT)) + } else { + null + } ShareouselUpdate( customActions, @@ -117,6 +122,8 @@ private fun readCallbackResponse(bundle: Bundle): ValidationResult, private val flags: ChooserServiceFlags, ) { suspend fun launch() { - targetIntentRepository.targetIntent - // TODO: maybe find a better way to exclude the initial intent (as here it's compared by - // reference) - .filter { it !== initialIntent } - .collect(::updateTargetIntent) + coroutineScope { + launch { + targetIntentRepository.targetIntent + // TODO: maybe find a better way to exclude the initial intent (as here it's + // compared by + // reference) + .filter { it !== initialIntent } + .collect(::updateTargetIntent) + } + + launch { + paramsUpdateRepository.updates.filterNotNull().collect(::updateChooserParameters) + } + } } private fun updateTargetIntent(targetIntent: Intent) { @@ -64,6 +80,38 @@ constructor( } } + private fun updateChooserParameters(update: ShareouselUpdate) { + chooserRequestRepository.update { current -> + ChooserRequest( + current.targetIntent, + current.targetAction, + current.isSendActionTarget, + current.targetType, + current.launchedFromPackage, + current.title, + current.defaultTitleResource, + current.referrer, + current.filteredComponentNames, + update.callerTargets ?: current.callerChooserTargets, + // chooser actions are handled separately + current.chooserActions, + update.modifyShareAction ?: current.modifyShareAction, + current.shouldRetainInOnStop, + update.alternateIntents ?: current.additionalTargets, + current.replacementExtras, + current.initialIntents, + update.resultIntentSender ?: current.chosenComponentSender, + update.refinementIntentSender ?: current.refinementIntentSender, + current.sharedText, + current.shareTargetFilter, + current.additionalContentUri, + current.focusedItemPosition, + current.contentTypeHint, + update.metadataText ?: current.metadataText, + ) + } + } + private fun ActivityModel.updateWithTargetIntent(targetIntent: Intent) = ActivityModel( Intent(intent).apply { putExtra(Intent.EXTRA_INTENT, targetIntent) }, -- cgit v1.2.3-59-g8ed1b