diff options
author | 2024-03-26 17:36:06 -0400 | |
---|---|---|
committer | 2024-03-28 15:42:29 -0400 | |
commit | 38a6a7637ed22bed196ede400ff72fea5407b17b (patch) | |
tree | 8799dd7642290f759083546f7c0bd36344b87acd | |
parent | 56c54af33d1e4a0e7d9215e90f8d5a6ba75b9ed6 (diff) |
Introduce ChooserRequestRepository
- Replace TargetIntentRepository with ChooserRequestRepository, using the ChooserRequest as the source of truth for the target intent.
- Caveat: custom actions are tracked separately to facilitate with testing; long-term we will want to update/replace ChooserRequest so that it isn't relying on un-mockable/un-fakeable types.
- Remove concept of "initialization" from repositories.
- Usages are better captured as "events", and so are handled in interactor codepaths that flow *into* the repositories.
Bug: 302691505
Flag: ACONFIG android.service.chooser.chooser_payload_toggling DEVELOPMENT
Test: atest IntentResolver-tests-unit
Change-Id: I8451a495478dbe750a44e6b049d4751fa7badf81
35 files changed, 682 insertions, 829 deletions
diff --git a/java/src/com/android/intentresolver/contentpreview/payloadtoggle/data/model/SelectionRecord.kt b/java/src/com/android/intentresolver/contentpreview/payloadtoggle/data/model/SelectionRecord.kt deleted file mode 100644 index c8fcb9d5..00000000 --- a/java/src/com/android/intentresolver/contentpreview/payloadtoggle/data/model/SelectionRecord.kt +++ /dev/null @@ -1,30 +0,0 @@ -/* - * 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.model - -import com.android.intentresolver.contentpreview.payloadtoggle.shared.model.PreviewModel - -data class SelectionRecord( - val type: SelectionRecordType, - val selection: Set<PreviewModel>, -) - -enum class SelectionRecordType { - Uninitialized, - Initial, - Updated, -} diff --git a/java/src/com/android/intentresolver/contentpreview/payloadtoggle/data/repository/ChooserParamsUpdateRepository.kt b/java/src/com/android/intentresolver/contentpreview/payloadtoggle/data/repository/PendingSelectionCallbackRepository.kt index 1a4f2b83..1745cd9c 100644 --- a/java/src/com/android/intentresolver/contentpreview/payloadtoggle/data/repository/ChooserParamsUpdateRepository.kt +++ b/java/src/com/android/intentresolver/contentpreview/payloadtoggle/data/repository/PendingSelectionCallbackRepository.kt @@ -1,5 +1,5 @@ /* - * Copyright 2024 The Android Open Source Project + * 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. @@ -16,19 +16,17 @@ package com.android.intentresolver.contentpreview.payloadtoggle.data.repository -import com.android.intentresolver.contentpreview.payloadtoggle.domain.model.ShareouselUpdate -import dagger.hilt.android.scopes.ViewModelScoped +import android.content.Intent +import dagger.hilt.android.scopes.ActivityRetainedScoped 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<ShareouselUpdate?>(null) - - fun setUpdates(update: ShareouselUpdate) { - updates.tryEmit(update) - } +/** Tracks active async communication with sharing app to notify of target intent update. */ +@ActivityRetainedScoped +class PendingSelectionCallbackRepository @Inject constructor() { + /** + * The target [Intent] that is has an active update request with the sharing app, or `null` if + * there is no active request. + */ + val pendingTargetIntent: MutableStateFlow<Intent?> = MutableStateFlow(null) } diff --git a/java/src/com/android/intentresolver/contentpreview/payloadtoggle/data/repository/PreviewSelectionsRepository.kt b/java/src/com/android/intentresolver/contentpreview/payloadtoggle/data/repository/PreviewSelectionsRepository.kt index b461d10b..9aecc981 100644 --- a/java/src/com/android/intentresolver/contentpreview/payloadtoggle/data/repository/PreviewSelectionsRepository.kt +++ b/java/src/com/android/intentresolver/contentpreview/payloadtoggle/data/repository/PreviewSelectionsRepository.kt @@ -16,52 +16,13 @@ package com.android.intentresolver.contentpreview.payloadtoggle.data.repository -import android.util.Log -import com.android.intentresolver.contentpreview.payloadtoggle.data.model.SelectionRecord -import com.android.intentresolver.contentpreview.payloadtoggle.data.model.SelectionRecordType.Initial -import com.android.intentresolver.contentpreview.payloadtoggle.data.model.SelectionRecordType.Uninitialized -import com.android.intentresolver.contentpreview.payloadtoggle.data.model.SelectionRecordType.Updated import com.android.intentresolver.contentpreview.payloadtoggle.shared.model.PreviewModel import dagger.hilt.android.scopes.ViewModelScoped import javax.inject.Inject import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.flow.update - -private const val TAG = "PreviewSelectionsRep" /** Stores set of selected previews. */ @ViewModelScoped class PreviewSelectionsRepository @Inject constructor() { - private val _selections = MutableStateFlow(SelectionRecord(Uninitialized, emptySet())) - - /** Selected previews data */ - val selections: StateFlow<SelectionRecord> = _selections.asStateFlow() - - fun setSelection(selection: Set<PreviewModel>) { - _selections.value = SelectionRecord(Initial, selection) - } - - fun select(item: PreviewModel) { - _selections.update { record -> - if (record.type == Uninitialized) { - Log.w(TAG, "Changing selection before it is initialized") - record - } else { - SelectionRecord(Updated, record.selection + item) - } - } - } - - fun unselect(item: PreviewModel) { - _selections.update { record -> - if (record.type == Uninitialized) { - Log.w(TAG, "Changing selection before it is initialized") - record - } else { - SelectionRecord(Updated, record.selection - item) - } - } - } + val selections = MutableStateFlow(emptySet<PreviewModel>()) } diff --git a/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/intent/TargetIntentModifier.kt b/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/intent/TargetIntentModifier.kt index 577dc34c..4a2a6932 100644 --- a/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/intent/TargetIntentModifier.kt +++ b/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/intent/TargetIntentModifier.kt @@ -32,7 +32,7 @@ import dagger.hilt.android.components.ViewModelComponent /** Modifies target intent based on current payload selection. */ fun interface TargetIntentModifier<Item> { - fun onSelectionChanged(selection: Collection<Item>): Intent + fun intentFromSelection(selection: Collection<Item>): Intent } class TargetIntentModifierImpl<Item>( @@ -40,7 +40,7 @@ class TargetIntentModifierImpl<Item>( private val getUri: Item.() -> Uri, private val getMimeType: Item.() -> String?, ) : TargetIntentModifier<Item> { - override fun onSelectionChanged(selection: Collection<Item>): Intent { + override fun intentFromSelection(selection: Collection<Item>): Intent { val uris = selection.mapTo(ArrayList()) { it.getUri() } val targetMimeType = selection.fold(null) { target: String?, item: Item -> diff --git a/java/src/com/android/intentresolver/contentpreview/payloadtoggle/data/repository/TargetIntentRepository.kt b/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/ChooserRequestInteractor.kt index bb43323a..61c04ac1 100644 --- a/java/src/com/android/intentresolver/contentpreview/payloadtoggle/data/repository/TargetIntentRepository.kt +++ b/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/ChooserRequestInteractor.kt @@ -14,32 +14,25 @@ * limitations under the License. */ -package com.android.intentresolver.contentpreview.payloadtoggle.data.repository +package com.android.intentresolver.contentpreview.payloadtoggle.domain.interactor import android.content.Intent import com.android.intentresolver.contentpreview.payloadtoggle.data.model.CustomActionModel -import com.android.intentresolver.contentpreview.payloadtoggle.data.model.TargetIntentRecord -import com.android.intentresolver.inject.TargetIntent -import dagger.hilt.android.scopes.ViewModelScoped +import com.android.intentresolver.v2.data.repository.ChooserRequestRepository import javax.inject.Inject -import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.asSharedFlow +import kotlinx.coroutines.flow.map /** Stores the target intent of the share sheet, and custom actions derived from the intent. */ -@ViewModelScoped -class TargetIntentRepository +class ChooserRequestInteractor @Inject constructor( - @TargetIntent initialIntent: Intent, - initialActions: List<CustomActionModel>, + private val repository: ChooserRequestRepository, ) { - val targetIntent = MutableStateFlow(TargetIntentRecord(isInitial = true, initialIntent)) + val targetIntent: Flow<Intent> + get() = repository.chooserRequest.map { it.targetIntent } - // TODO: this can probably be derived from [targetIntent]; right now, the [initialActions] are - // coming from a different place (ChooserRequest) than later ones (SelectionChangeCallback) - // and so this serves as the source of truth between the two. - val customActions = MutableStateFlow(initialActions) - - fun updateTargetIntent(intent: Intent) { - targetIntent.value = TargetIntentRecord(isInitial = false, intent) - } + val customActions: Flow<List<CustomActionModel>> + get() = repository.customActions.asSharedFlow() } diff --git a/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/CustomActionsInteractor.kt b/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/CustomActionsInteractor.kt index 56f781fb..e973e844 100644 --- a/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/CustomActionsInteractor.kt +++ b/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/CustomActionsInteractor.kt @@ -21,7 +21,6 @@ import android.content.ContentResolver import android.content.pm.PackageManager import com.android.intentresolver.contentpreview.payloadtoggle.data.model.CustomActionModel import com.android.intentresolver.contentpreview.payloadtoggle.data.repository.ActivityResultRepository -import com.android.intentresolver.contentpreview.payloadtoggle.data.repository.TargetIntentRepository import com.android.intentresolver.contentpreview.payloadtoggle.domain.model.ActionModel import com.android.intentresolver.icon.toComposeIcon import com.android.intentresolver.inject.Background @@ -41,12 +40,12 @@ constructor( private val contentResolver: ContentResolver, private val eventLog: EventLog, private val packageManager: PackageManager, - private val targetIntentRepo: TargetIntentRepository, + private val chooserRequestInteractor: ChooserRequestInteractor, ) { /** List of [ActionModel] that can be presented in Shareousel. */ val customActions: Flow<List<ActionModel>> get() = - targetIntentRepo.customActions + chooserRequestInteractor.customActions .map { actions -> actions.map { action -> ActionModel( diff --git a/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/FetchPreviewsInteractor.kt b/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/FetchPreviewsInteractor.kt index a7749c92..9bc7ae63 100644 --- a/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/FetchPreviewsInteractor.kt +++ b/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/FetchPreviewsInteractor.kt @@ -41,10 +41,10 @@ constructor( private val uriMetadataReader: UriMetadataReader, @PayloadToggle private val cursorResolver: CursorResolver<@JvmSuppressWildcards Uri?>, ) { - suspend fun launch() = coroutineScope { + suspend fun activate() = coroutineScope { val cursor = async { cursorResolver.getCursor() } val initialPreviewMap: Set<PreviewModel> = getInitialPreviews() - selectionRepository.setSelection(initialPreviewMap) + selectionRepository.selections.value = initialPreviewMap setCursorPreviews.setPreviews( previewsByKey = initialPreviewMap, startIndex = focusedItemIdx, diff --git a/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/ProcessTargetIntentUpdatesInteractor.kt b/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/ProcessTargetIntentUpdatesInteractor.kt new file mode 100644 index 00000000..04416a3d --- /dev/null +++ b/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/ProcessTargetIntentUpdatesInteractor.kt @@ -0,0 +1,42 @@ +/* + * 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.intentresolver.contentpreview.payloadtoggle.domain.interactor + +import com.android.intentresolver.contentpreview.payloadtoggle.data.repository.PendingSelectionCallbackRepository +import com.android.intentresolver.contentpreview.payloadtoggle.domain.update.SelectionChangeCallback +import javax.inject.Inject +import kotlinx.coroutines.flow.collectLatest + +/** Communicates with the sharing application to notify of changes to the target intent. */ +class ProcessTargetIntentUpdatesInteractor +@Inject +constructor( + private val selectionCallback: SelectionChangeCallback, + private val repository: PendingSelectionCallbackRepository, + private val chooserRequestInteractor: UpdateChooserRequestInteractor, +) { + /** Listen for events and update state. */ + suspend fun activate() { + repository.pendingTargetIntent.collectLatest { targetIntent -> + targetIntent ?: return@collectLatest + selectionCallback.onSelectionChanged(targetIntent)?.let { update -> + chooserRequestInteractor.applyUpdate(update) + } + repository.pendingTargetIntent.compareAndSet(targetIntent, null) + } + } +} diff --git a/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/SelectablePreviewInteractor.kt b/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/SelectablePreviewInteractor.kt index 3b5b0ddf..55a995f5 100644 --- a/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/SelectablePreviewInteractor.kt +++ b/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/SelectablePreviewInteractor.kt @@ -17,7 +17,6 @@ package com.android.intentresolver.contentpreview.payloadtoggle.domain.interactor import android.net.Uri -import com.android.intentresolver.contentpreview.payloadtoggle.data.repository.PreviewSelectionsRepository import com.android.intentresolver.contentpreview.payloadtoggle.shared.model.PreviewModel import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map @@ -25,19 +24,19 @@ import kotlinx.coroutines.flow.map /** An individual preview in Shareousel. */ class SelectablePreviewInteractor( private val key: PreviewModel, - private val selectionRepo: PreviewSelectionsRepository, + private val selectionInteractor: SelectionInteractor, ) { val uri: Uri = key.uri /** Whether or not this preview is selected by the user. */ - val isSelected: Flow<Boolean> = selectionRepo.selections.map { key in it.selection } + val isSelected: Flow<Boolean> = selectionInteractor.selections.map { key in it } /** Sets whether this preview is selected by the user. */ fun setSelected(isSelected: Boolean) { if (isSelected) { - selectionRepo.select(key) + selectionInteractor.select(key) } else { - selectionRepo.unselect(key) + selectionInteractor.unselect(key) } } } diff --git a/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/SelectablePreviewsInteractor.kt b/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/SelectablePreviewsInteractor.kt index 78e208f6..a578d0e2 100644 --- a/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/SelectablePreviewsInteractor.kt +++ b/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/SelectablePreviewsInteractor.kt @@ -17,7 +17,6 @@ package com.android.intentresolver.contentpreview.payloadtoggle.domain.interactor import com.android.intentresolver.contentpreview.payloadtoggle.data.repository.CursorPreviewsRepository -import com.android.intentresolver.contentpreview.payloadtoggle.data.repository.PreviewSelectionsRepository import com.android.intentresolver.contentpreview.payloadtoggle.shared.model.PreviewModel import com.android.intentresolver.contentpreview.payloadtoggle.shared.model.PreviewsModel import javax.inject.Inject @@ -27,7 +26,7 @@ class SelectablePreviewsInteractor @Inject constructor( private val previewsRepo: CursorPreviewsRepository, - private val selectionRepo: PreviewSelectionsRepository, + private val selectionInteractor: SelectionInteractor, ) { /** Keys of previews available for display in Shareousel. */ val previews: Flow<PreviewsModel?> @@ -37,6 +36,5 @@ constructor( * Returns a [SelectablePreviewInteractor] that can be used to interact with the individual * preview associated with [key]. */ - fun preview(key: PreviewModel) = - SelectablePreviewInteractor(key = key, selectionRepo = selectionRepo) + fun preview(key: PreviewModel) = SelectablePreviewInteractor(key, selectionInteractor) } diff --git a/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/SelectionInteractor.kt b/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/SelectionInteractor.kt index 0b8bcdd7..a570f36e 100644 --- a/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/SelectionInteractor.kt +++ b/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/SelectionInteractor.kt @@ -17,15 +17,38 @@ package com.android.intentresolver.contentpreview.payloadtoggle.domain.interactor import com.android.intentresolver.contentpreview.payloadtoggle.data.repository.PreviewSelectionsRepository +import com.android.intentresolver.contentpreview.payloadtoggle.domain.intent.TargetIntentModifier +import com.android.intentresolver.contentpreview.payloadtoggle.shared.model.PreviewModel import javax.inject.Inject import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.updateAndGet class SelectionInteractor @Inject constructor( - selectionRepo: PreviewSelectionsRepository, + private val selectionsRepo: PreviewSelectionsRepository, + private val targetIntentModifier: TargetIntentModifier<PreviewModel>, + private val updateTargetIntentInteractor: UpdateTargetIntentInteractor, ) { + /** Set of selected previews. */ + val selections: StateFlow<Set<PreviewModel>> + get() = selectionsRepo.selections + /** Amount of selected previews. */ - val amountSelected: Flow<Int> = selectionRepo.selections.map { it.selection.size } + val amountSelected: Flow<Int> = selectionsRepo.selections.map { it.size } + + fun select(model: PreviewModel) { + updateChooserRequest(selectionsRepo.selections.updateAndGet { it + model }) + } + + fun unselect(model: PreviewModel) { + updateChooserRequest(selectionsRepo.selections.updateAndGet { it - model }) + } + + private fun updateChooserRequest(selections: Set<PreviewModel>) { + val intent = targetIntentModifier.intentFromSelection(selections) + updateTargetIntentInteractor.updateTargetIntent(intent) + } } diff --git a/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/UpdateChooserRequestInteractor.kt b/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/UpdateChooserRequestInteractor.kt new file mode 100644 index 00000000..9e48cd28 --- /dev/null +++ b/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/UpdateChooserRequestInteractor.kt @@ -0,0 +1,62 @@ +/* + * 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.intentresolver.contentpreview.payloadtoggle.domain.interactor + +import android.content.Intent +import com.android.intentresolver.contentpreview.payloadtoggle.domain.intent.CustomAction +import com.android.intentresolver.contentpreview.payloadtoggle.domain.intent.PendingIntentSender +import com.android.intentresolver.contentpreview.payloadtoggle.domain.intent.toCustomActionModel +import com.android.intentresolver.contentpreview.payloadtoggle.domain.model.ShareouselUpdate +import com.android.intentresolver.contentpreview.payloadtoggle.domain.model.getOrDefault +import com.android.intentresolver.contentpreview.payloadtoggle.domain.model.onValue +import com.android.intentresolver.v2.data.repository.ChooserRequestRepository +import javax.inject.Inject +import kotlinx.coroutines.flow.update + +/** Updates the tracked chooser request. */ +class UpdateChooserRequestInteractor +@Inject +constructor( + private val repository: ChooserRequestRepository, + @CustomAction private val pendingIntentSender: PendingIntentSender, +) { + fun applyUpdate(update: ShareouselUpdate) { + repository.chooserRequest.update { current -> + current.copy( + callerChooserTargets = + update.callerTargets.getOrDefault(current.callerChooserTargets), + modifyShareAction = + update.modifyShareAction.getOrDefault(current.modifyShareAction), + additionalTargets = update.alternateIntents.getOrDefault(current.additionalTargets), + chosenComponentSender = + update.resultIntentSender.getOrDefault(current.chosenComponentSender), + refinementIntentSender = + update.refinementIntentSender.getOrDefault(current.refinementIntentSender), + metadataText = update.metadataText.getOrDefault(current.metadataText), + chooserActions = update.customActions.getOrDefault(current.chooserActions), + ) + } + update.customActions.onValue { actions -> + repository.customActions.value = + actions.map { it.toCustomActionModel(pendingIntentSender) } + } + } + + fun setTargetIntent(targetIntent: Intent) { + repository.chooserRequest.update { it.copy(targetIntent = targetIntent) } + } +} 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 06e28cba..429e34e9 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 @@ -14,64 +14,24 @@ * limitations under the License. */ -@file:OptIn(ExperimentalCoroutinesApi::class) - package com.android.intentresolver.contentpreview.payloadtoggle.domain.interactor -import com.android.intentresolver.contentpreview.payloadtoggle.data.model.SelectionRecordType -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 -import com.android.intentresolver.contentpreview.payloadtoggle.domain.intent.PendingIntentSender -import com.android.intentresolver.contentpreview.payloadtoggle.domain.intent.TargetIntentModifier -import com.android.intentresolver.contentpreview.payloadtoggle.domain.intent.toCustomActionModel -import com.android.intentresolver.contentpreview.payloadtoggle.domain.model.onValue -import com.android.intentresolver.contentpreview.payloadtoggle.domain.update.SelectionChangeCallback -import com.android.intentresolver.contentpreview.payloadtoggle.shared.model.PreviewModel +import android.content.Intent +import com.android.intentresolver.contentpreview.payloadtoggle.data.repository.PendingSelectionCallbackRepository import javax.inject.Inject -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.coroutineScope -import kotlinx.coroutines.flow.collectLatest -import kotlinx.coroutines.flow.filter -import kotlinx.coroutines.flow.filterNotNull -import kotlinx.coroutines.flow.mapLatest -import kotlinx.coroutines.launch -/** Updates [TargetIntentRepository] in reaction to user selection changes. */ 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, - private val targetIntentModifier: TargetIntentModifier<PreviewModel>, + private val repository: PendingSelectionCallbackRepository, + private val chooserRequestInteractor: UpdateChooserRequestInteractor, ) { - /** Listen for events and update state. */ - suspend fun launch(): Unit = coroutineScope { - launch { - intentRepository.targetIntent - .filter { !it.isInitial } - .mapLatest { record -> selectionCallback.onSelectionChanged(record.intent) } - .filterNotNull() - .collect { updates -> - updates.customActions.onValue { actions -> - intentRepository.customActions.value = - actions.map { it.toCustomActionModel(pendingIntentSender) } - } - chooserParamsUpdateRepository.setUpdates(updates) - } - } - launch { - selectionRepo.selections - .filter { it.type == SelectionRecordType.Updated } - .collectLatest { - intentRepository.updateTargetIntent( - targetIntentModifier.onSelectionChanged(it.selection) - ) - } - } + /** + * Updates the target intent for the chooser. This will kick off an asynchronous IPC with the + * sharing application, so that it can react to the new intent. + */ + fun updateTargetIntent(targetIntent: Intent) { + chooserRequestInteractor.setTargetIntent(targetIntent) + repository.pendingTargetIntent.value = targetIntent } } diff --git a/java/src/com/android/intentresolver/inject/ActivityModelModule.kt b/java/src/com/android/intentresolver/inject/ActivityModelModule.kt index c08c7f4c..ff2bb14b 100644 --- a/java/src/com/android/intentresolver/inject/ActivityModelModule.kt +++ b/java/src/com/android/intentresolver/inject/ActivityModelModule.kt @@ -21,8 +21,8 @@ import android.net.Uri import android.service.chooser.ChooserAction import androidx.lifecycle.SavedStateHandle import com.android.intentresolver.util.ownedByCurrentUser +import com.android.intentresolver.v2.data.model.ChooserRequest import com.android.intentresolver.v2.ui.model.ActivityModel -import com.android.intentresolver.v2.ui.model.ChooserRequest import com.android.intentresolver.v2.ui.viewmodel.readChooserRequest import com.android.intentresolver.v2.validation.Valid import com.android.intentresolver.v2.validation.ValidationResult @@ -48,12 +48,20 @@ object ActivityModelModule { @Provides @ViewModelScoped - fun provideChooserRequest( + fun provideInitialRequest( activityModel: ActivityModel, flags: ChooserServiceFlags, ): ValidationResult<ChooserRequest> = readChooserRequest(activityModel, flags) @Provides + fun provideChooserRequest( + initialRequest: ValidationResult<ChooserRequest>, + ): ChooserRequest = + requireNotNull((initialRequest as? Valid)?.value) { + "initialRequest is Invalid, no chooser request available" + } + + @Provides @TargetIntent fun targetIntent(chooserReq: ValidationResult<ChooserRequest>): Intent = requireNotNull((chooserReq as? Valid)?.value?.targetIntent) { "no target intent available" } diff --git a/java/src/com/android/intentresolver/v2/ChooserActivity.java b/java/src/com/android/intentresolver/v2/ChooserActivity.java index ffa0469c..d624c9e4 100644 --- a/java/src/com/android/intentresolver/v2/ChooserActivity.java +++ b/java/src/com/android/intentresolver/v2/ChooserActivity.java @@ -131,6 +131,7 @@ import com.android.intentresolver.model.AppPredictionServiceResolverComparator; import com.android.intentresolver.model.ResolverRankerServiceResolverComparator; import com.android.intentresolver.shortcuts.AppPredictorFactory; import com.android.intentresolver.shortcuts.ShortcutLoader; +import com.android.intentresolver.v2.data.model.ChooserRequest; import com.android.intentresolver.v2.data.repository.DevicePolicyResources; import com.android.intentresolver.v2.domain.interactor.UserInteractor; import com.android.intentresolver.v2.emptystate.NoAppsAvailableEmptyStateProvider; @@ -151,7 +152,6 @@ import com.android.intentresolver.v2.ui.ProfilePagerResources; import com.android.intentresolver.v2.ui.ShareResultSender; import com.android.intentresolver.v2.ui.ShareResultSenderFactory; import com.android.intentresolver.v2.ui.model.ActivityModel; -import com.android.intentresolver.v2.ui.model.ChooserRequest; import com.android.intentresolver.v2.ui.viewmodel.ChooserViewModel; import com.android.intentresolver.widget.ActionRow; import com.android.intentresolver.widget.ImagePreviewView; diff --git a/java/src/com/android/intentresolver/v2/ChooserHelper.kt b/java/src/com/android/intentresolver/v2/ChooserHelper.kt index f2a2726a..503e46d8 100644 --- a/java/src/com/android/intentresolver/v2/ChooserHelper.kt +++ b/java/src/com/android/intentresolver/v2/ChooserHelper.kt @@ -29,9 +29,9 @@ import androidx.lifecycle.repeatOnLifecycle import com.android.intentresolver.contentpreview.payloadtoggle.data.repository.ActivityResultRepository import com.android.intentresolver.inject.Background import com.android.intentresolver.v2.annotation.JavaInterop +import com.android.intentresolver.v2.data.model.ChooserRequest import com.android.intentresolver.v2.domain.interactor.UserInteractor import com.android.intentresolver.v2.shared.model.Profile -import com.android.intentresolver.v2.ui.model.ChooserRequest import com.android.intentresolver.v2.ui.viewmodel.ChooserViewModel import com.android.intentresolver.v2.validation.Invalid import com.android.intentresolver.v2.validation.Valid diff --git a/java/src/com/android/intentresolver/v2/ui/model/ChooserRequest.kt b/java/src/com/android/intentresolver/v2/data/model/ChooserRequest.kt index 4f3cf3cd..7c9c8613 100644 --- a/java/src/com/android/intentresolver/v2/ui/model/ChooserRequest.kt +++ b/java/src/com/android/intentresolver/v2/data/model/ChooserRequest.kt @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.intentresolver.v2.ui.model +package com.android.intentresolver.v2.data.model import android.content.ComponentName import android.content.Intent diff --git a/java/src/com/android/intentresolver/v2/data/repository/ChooserRequestRepository.kt b/java/src/com/android/intentresolver/v2/data/repository/ChooserRequestRepository.kt new file mode 100644 index 00000000..d23e07ee --- /dev/null +++ b/java/src/com/android/intentresolver/v2/data/repository/ChooserRequestRepository.kt @@ -0,0 +1,39 @@ +/* + * 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.intentresolver.v2.data.repository + +import com.android.intentresolver.contentpreview.payloadtoggle.data.model.CustomActionModel +import com.android.intentresolver.v2.data.model.ChooserRequest +import dagger.hilt.android.scopes.ViewModelScoped +import javax.inject.Inject +import kotlinx.coroutines.flow.MutableStateFlow + +@ViewModelScoped +class ChooserRequestRepository +@Inject +constructor( + initialRequest: ChooserRequest, + initialActions: List<CustomActionModel>, +) { + /** All information from the sharing application pertaining to the chooser. */ + val chooserRequest: MutableStateFlow<ChooserRequest> = MutableStateFlow(initialRequest) + + /** Custom actions from the sharing app to be presented in the chooser. */ + // NOTE: this could be derived directly from chooserRequest, but that would require working + // directly with PendingIntents, which complicates testing. + val customActions: MutableStateFlow<List<CustomActionModel>> = MutableStateFlow(initialActions) +} diff --git a/java/src/com/android/intentresolver/v2/domain/interactor/ChooserRequestUpdateInteractor.kt b/java/src/com/android/intentresolver/v2/domain/interactor/ChooserRequestUpdateInteractor.kt deleted file mode 100644 index 37213403..00000000 --- a/java/src/com/android/intentresolver/v2/domain/interactor/ChooserRequestUpdateInteractor.kt +++ /dev/null @@ -1,91 +0,0 @@ -/* - * 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.v2.domain.interactor - -import android.content.Intent -import com.android.intentresolver.contentpreview.payloadtoggle.data.repository.ChooserParamsUpdateRepository -import com.android.intentresolver.contentpreview.payloadtoggle.data.repository.TargetIntentRepository -import com.android.intentresolver.contentpreview.payloadtoggle.domain.model.ShareouselUpdate -import com.android.intentresolver.contentpreview.payloadtoggle.domain.model.getOrDefault -import com.android.intentresolver.v2.ui.model.ChooserRequest -import dagger.assisted.Assisted -import dagger.assisted.AssistedFactory -import dagger.assisted.AssistedInject -import dagger.hilt.android.scopes.ViewModelScoped -import kotlinx.coroutines.coroutineScope -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.filter -import kotlinx.coroutines.flow.filterNotNull -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.update -import kotlinx.coroutines.launch - -/** Updates updates ChooserRequest with a new target intent */ -// TODO: make fully injectable -class ChooserRequestUpdateInteractor -@AssistedInject -constructor( - private val targetIntentRepository: TargetIntentRepository, - private val paramsUpdateRepository: ChooserParamsUpdateRepository, - // TODO: replace with a proper repository, when available - @Assisted private val chooserRequestRepository: MutableStateFlow<ChooserRequest>, -) { - - suspend fun launch() { - coroutineScope { - launch { - targetIntentRepository.targetIntent - .filter { !it.isInitial } - .map { it.intent } - .collect(::updateTargetIntent) - } - - launch { - paramsUpdateRepository.updates.filterNotNull().collect(::updateChooserParameters) - } - } - } - - private fun updateTargetIntent(targetIntent: Intent) { - chooserRequestRepository.update { current -> current.copy(targetIntent = targetIntent) } - } - - private fun updateChooserParameters(update: ShareouselUpdate) { - chooserRequestRepository.update { current -> - current.copy( - callerChooserTargets = - update.callerTargets.getOrDefault(current.callerChooserTargets), - modifyShareAction = - update.modifyShareAction.getOrDefault(current.modifyShareAction), - additionalTargets = update.alternateIntents.getOrDefault(current.additionalTargets), - chosenComponentSender = - update.resultIntentSender.getOrDefault(current.chosenComponentSender), - refinementIntentSender = - update.refinementIntentSender.getOrDefault(current.refinementIntentSender), - metadataText = update.metadataText.getOrDefault(current.metadataText), - ) - } - } -} - -@AssistedFactory -@ViewModelScoped -interface ChooserRequestUpdateInteractorFactory { - fun create( - chooserRequestRepository: MutableStateFlow<ChooserRequest> - ): ChooserRequestUpdateInteractor -} diff --git a/java/src/com/android/intentresolver/v2/ui/model/ActivityModel.kt b/java/src/com/android/intentresolver/v2/ui/model/ActivityModel.kt index 07b17435..67c2a25e 100644 --- a/java/src/com/android/intentresolver/v2/ui/model/ActivityModel.kt +++ b/java/src/com/android/intentresolver/v2/ui/model/ActivityModel.kt @@ -20,6 +20,7 @@ import android.content.Intent import android.net.Uri import android.os.Parcel import android.os.Parcelable +import com.android.intentresolver.v2.data.model.ANDROID_APP_SCHEME import com.android.intentresolver.v2.ext.readParcelable import com.android.intentresolver.v2.ext.requireParcelable import java.util.Objects 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 7ebf65a9..a25fcbea 100644 --- a/java/src/com/android/intentresolver/v2/ui/viewmodel/ChooserRequestReader.kt +++ b/java/src/com/android/intentresolver/v2/ui/viewmodel/ChooserRequestReader.kt @@ -44,10 +44,10 @@ import com.android.intentresolver.ContentTypeHint import com.android.intentresolver.R import com.android.intentresolver.inject.ChooserServiceFlags import com.android.intentresolver.util.hasValidIcon +import com.android.intentresolver.v2.data.model.ChooserRequest import com.android.intentresolver.v2.ext.hasSendAction import com.android.intentresolver.v2.ext.ifMatch import com.android.intentresolver.v2.ui.model.ActivityModel -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 diff --git a/java/src/com/android/intentresolver/v2/ui/viewmodel/ChooserViewModel.kt b/java/src/com/android/intentresolver/v2/ui/viewmodel/ChooserViewModel.kt index 4431a545..e39329b1 100644 --- a/java/src/com/android/intentresolver/v2/ui/viewmodel/ChooserViewModel.kt +++ b/java/src/com/android/intentresolver/v2/ui/viewmodel/ChooserViewModel.kt @@ -20,21 +20,21 @@ import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.android.intentresolver.contentpreview.payloadtoggle.domain.interactor.FetchPreviewsInteractor -import com.android.intentresolver.contentpreview.payloadtoggle.domain.interactor.UpdateTargetIntentInteractor +import com.android.intentresolver.contentpreview.payloadtoggle.domain.interactor.ProcessTargetIntentUpdatesInteractor import com.android.intentresolver.contentpreview.payloadtoggle.ui.viewmodel.ShareouselViewModel import com.android.intentresolver.inject.Background import com.android.intentresolver.inject.ChooserServiceFlags -import com.android.intentresolver.v2.domain.interactor.ChooserRequestUpdateInteractorFactory +import com.android.intentresolver.v2.data.model.ChooserRequest +import com.android.intentresolver.v2.data.repository.ChooserRequestRepository import com.android.intentresolver.v2.ui.model.ActivityModel import com.android.intentresolver.v2.ui.model.ActivityModel.Companion.ACTIVITY_MODEL_KEY -import com.android.intentresolver.v2.ui.model.ChooserRequest import com.android.intentresolver.v2.validation.Invalid import com.android.intentresolver.v2.validation.Valid +import com.android.intentresolver.v2.validation.ValidationResult import dagger.Lazy import dagger.hilt.android.lifecycle.HiltViewModel import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher -import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.launch @@ -47,11 +47,17 @@ class ChooserViewModel constructor( args: SavedStateHandle, private val shareouselViewModelProvider: Lazy<ShareouselViewModel>, - private val updateTargetIntentInteractor: Lazy<UpdateTargetIntentInteractor>, + private val processUpdatesInteractor: Lazy<ProcessTargetIntentUpdatesInteractor>, private val fetchPreviewsInteractor: Lazy<FetchPreviewsInteractor>, @Background private val bgDispatcher: CoroutineDispatcher, - private val chooserRequestUpdateInteractorFactory: ChooserRequestUpdateInteractorFactory, private val flags: ChooserServiceFlags, + /** + * Provided only for the express purpose of early exit in the event of an invalid request. + * + * Note: [request] can only be safely accessed after checking if this value is [Valid]. + */ + val initialRequest: ValidationResult<ChooserRequest>, + private val chooserRequestRepository: Lazy<ChooserRequestRepository>, ) : ViewModel() { /** Parcelable-only references provided from the creating Activity */ @@ -60,43 +66,29 @@ constructor( "ActivityModel missing in SavedStateHandle! ($ACTIVITY_MODEL_KEY)" } - val shareouselViewModel by lazy { + val shareouselViewModel: ShareouselViewModel by lazy { // TODO: consolidate this logic, this would require a consolidated preview view model but // for now just postpone starting the payload selection preview machinery until it's needed assert(flags.chooserPayloadToggling()) { "An attempt to use payload selection preview with the disabled flag" } - viewModelScope.launch(bgDispatcher) { updateTargetIntentInteractor.get().launch() } - viewModelScope.launch(bgDispatcher) { fetchPreviewsInteractor.get().launch() } - viewModelScope.launch { chooserRequestUpdateInteractorFactory.create(_request).launch() } + viewModelScope.launch(bgDispatcher) { processUpdatesInteractor.get().activate() } + viewModelScope.launch(bgDispatcher) { fetchPreviewsInteractor.get().activate() } shareouselViewModelProvider.get() } /** - * Provided only for the express purpose of early exit in the event of an invalid request. - * - * Note: [request] can only be safely accessed after checking if this value is [Valid]. - */ - internal val initialRequest = readChooserRequest(activityModel, flags) - - private lateinit var _request: MutableStateFlow<ChooserRequest> - - /** * A [StateFlow] of [ChooserRequest]. * * Note: Only safe to access after checking if [initialRequest] is [Valid]. */ - lateinit var request: StateFlow<ChooserRequest> - private set + val request: StateFlow<ChooserRequest> + get() = chooserRequestRepository.get().chooserRequest.asStateFlow() init { - when (initialRequest) { - is Valid -> { - _request = MutableStateFlow(initialRequest.value) - request = _request.asStateFlow() - } - is Invalid -> Log.w(TAG, "initialRequest is Invalid, initialization failed") + if (initialRequest is Invalid) { + Log.w(TAG, "initialRequest is Invalid, initialization failed") } } } diff --git a/tests/unit/src/com/android/intentresolver/contentpreview/payloadtoggle/data/repository/PreviewSelectionsRepositoryTest.kt b/tests/unit/src/com/android/intentresolver/contentpreview/payloadtoggle/data/repository/PreviewSelectionsRepositoryTest.kt deleted file mode 100644 index 48c8b58a..00000000 --- a/tests/unit/src/com/android/intentresolver/contentpreview/payloadtoggle/data/repository/PreviewSelectionsRepositoryTest.kt +++ /dev/null @@ -1,43 +0,0 @@ -/* - * 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 android.net.Uri -import com.android.intentresolver.contentpreview.payloadtoggle.data.model.SelectionRecordType.Initial -import com.android.intentresolver.contentpreview.payloadtoggle.data.model.SelectionRecordType.Uninitialized -import com.android.intentresolver.contentpreview.payloadtoggle.data.model.SelectionRecordType.Updated -import com.android.intentresolver.contentpreview.payloadtoggle.shared.model.PreviewModel -import com.google.common.truth.Truth.assertThat -import org.junit.Test - -class PreviewSelectionsRepositoryTest { - - @Test - fun testSelectionStatus() { - val testSubject = PreviewSelectionsRepository() - - assertThat(testSubject.selections.value.type).isEqualTo(Uninitialized) - - testSubject.setSelection(setOf(PreviewModel(Uri.parse("content://pkg/1.png"), "image/png"))) - - assertThat(testSubject.selections.value.type).isEqualTo(Initial) - - testSubject.select(PreviewModel(Uri.parse("content://pkg/2.png"), "image/png")) - - assertThat(testSubject.selections.value.type).isEqualTo(Updated) - } -} diff --git a/tests/unit/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/intent/TargetIntentModifierImplTest.kt b/tests/unit/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/intent/TargetIntentModifierImplTest.kt index f4be47ed..7c36ef55 100644 --- a/tests/unit/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/intent/TargetIntentModifierImplTest.kt +++ b/tests/unit/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/intent/TargetIntentModifierImplTest.kt @@ -32,14 +32,14 @@ class TargetIntentModifierImplTest { val u1 = createUri(1) val u2 = createUri(2) - testSubject.onSelectionChanged(listOf(u1, u2)).let { intent -> + testSubject.intentFromSelection(listOf(u1, u2)).let { intent -> assertThat(intent.action).isEqualTo(ACTION_SEND_MULTIPLE) assertThat(intent.getParcelableArrayListExtra(EXTRA_STREAM, Uri::class.java)) .containsExactly(u1, u2) .inOrder() } - testSubject.onSelectionChanged(listOf(u1)).let { intent -> + testSubject.intentFromSelection(listOf(u1)).let { intent -> assertThat(intent.action).isEqualTo(ACTION_SEND) assertThat(intent.getParcelableExtra(EXTRA_STREAM, Uri::class.java)).isEqualTo(u1) } @@ -52,20 +52,22 @@ class TargetIntentModifierImplTest { val u1 = createUri(1) val u2 = createUri(2) - testSubject.onSelectionChanged(listOf(u1 to "image/png", u2 to "image/png")).let { intent -> + testSubject.intentFromSelection(listOf(u1 to "image/png", u2 to "image/png")).let { intent + -> assertThat(intent.type).isEqualTo("image/png") } - testSubject.onSelectionChanged(listOf(u1 to "image/png", u2 to "image/jpg")).let { intent -> + testSubject.intentFromSelection(listOf(u1 to "image/png", u2 to "image/jpg")).let { intent + -> assertThat(intent.type).isEqualTo("image/*") } - testSubject.onSelectionChanged(listOf(u1 to "image/png", u2 to "video/mpeg")).let { intent + testSubject.intentFromSelection(listOf(u1 to "image/png", u2 to "video/mpeg")).let { intent -> assertThat(intent.type).isEqualTo("*/*") } - testSubject.onSelectionChanged(listOf(u1 to "image/png", u2 to null)).let { intent -> + testSubject.intentFromSelection(listOf(u1 to "image/png", u2 to null)).let { intent -> assertThat(intent.type).isEqualTo("*/*") } } diff --git a/tests/unit/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/CustomActionsInteractorTest.kt b/tests/unit/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/CustomActionsInteractorTest.kt index 95ad966e..ceb20dab 100644 --- a/tests/unit/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/CustomActionsInteractorTest.kt +++ b/tests/unit/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/CustomActionsInteractorTest.kt @@ -19,16 +19,16 @@ package com.android.intentresolver.contentpreview.payloadtoggle.domain.interactor import android.app.Activity -import android.content.Intent import android.graphics.Bitmap import android.graphics.drawable.Icon import com.android.intentresolver.contentpreview.payloadtoggle.data.model.CustomActionModel import com.android.intentresolver.contentpreview.payloadtoggle.data.repository.ActivityResultRepository -import com.android.intentresolver.contentpreview.payloadtoggle.data.repository.TargetIntentRepository import com.android.intentresolver.contentpreview.payloadtoggle.domain.model.ActionModel import com.android.intentresolver.icon.BitmapIcon import com.android.intentresolver.mock import com.android.intentresolver.util.comparingElementsUsingTransform +import com.android.intentresolver.v2.data.model.fakeChooserRequest +import com.android.intentresolver.v2.data.repository.ChooserRequestRepository import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.StateFlow @@ -47,7 +47,14 @@ class CustomActionsInteractorTest { runTest(testDispatcher) { val bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ALPHA_8) val icon = Icon.createWithBitmap(bitmap) - val chooserActions = listOf(CustomActionModel("label1", icon) {}) + val chooserRequestRepository = + ChooserRequestRepository( + initialRequest = fakeChooserRequest(), + initialActions = + listOf( + CustomActionModel(label = "label1", icon = icon, performAction = {}), + ), + ) val underTest = CustomActionsInteractor( activityResultRepo = ActivityResultRepository(), @@ -55,11 +62,8 @@ class CustomActionsInteractorTest { contentResolver = mock {}, eventLog = mock {}, packageManager = mock {}, - targetIntentRepo = - TargetIntentRepository( - initialIntent = Intent(), - initialActions = chooserActions, - ), + chooserRequestInteractor = + ChooserRequestInteractor(repository = chooserRequestRepository), ) val customActions: StateFlow<List<ActionModel>> = underTest.customActions.stateIn(backgroundScope) @@ -80,9 +84,9 @@ class CustomActionsInteractorTest { @Test fun customActions_tracksRepoUpdates() = runTest(testDispatcher) { - val targetIntentRepository = - TargetIntentRepository( - initialIntent = Intent(), + val chooserRequestRepository = + ChooserRequestRepository( + initialRequest = fakeChooserRequest(), initialActions = emptyList(), ) val underTest = @@ -92,7 +96,8 @@ class CustomActionsInteractorTest { contentResolver = mock {}, eventLog = mock {}, packageManager = mock {}, - targetIntentRepo = targetIntentRepository, + chooserRequestInteractor = + ChooserRequestInteractor(repository = chooserRequestRepository), ) val customActions: StateFlow<List<ActionModel>> = @@ -100,7 +105,7 @@ class CustomActionsInteractorTest { val bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ALPHA_8) val icon = Icon.createWithBitmap(bitmap) val chooserActions = listOf(CustomActionModel("label1", icon) {}) - targetIntentRepository.customActions.value = chooserActions + chooserRequestRepository.customActions.value = chooserActions runCurrent() assertThat(customActions.value) @@ -123,8 +128,19 @@ class CustomActionsInteractorTest { val bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ALPHA_8) val icon = Icon.createWithBitmap(bitmap) var actionSent = false - val chooserActions = listOf(CustomActionModel("label1", icon) { actionSent = true }) val activityResultRepository = ActivityResultRepository() + val chooserRequestRepository = + ChooserRequestRepository( + initialRequest = fakeChooserRequest(), + initialActions = + listOf( + CustomActionModel( + label = "label1", + icon = icon, + performAction = { actionSent = true }, + ) + ), + ) val underTest = CustomActionsInteractor( activityResultRepo = activityResultRepository, @@ -132,10 +148,9 @@ class CustomActionsInteractorTest { contentResolver = mock {}, eventLog = mock {}, packageManager = mock {}, - targetIntentRepo = - TargetIntentRepository( - initialIntent = Intent(), - initialActions = chooserActions, + chooserRequestInteractor = + ChooserRequestInteractor( + repository = chooserRequestRepository, ), ) val customActions: StateFlow<List<ActionModel>> = diff --git a/tests/unit/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/FetchPreviewsInteractorTest.kt b/tests/unit/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/FetchPreviewsInteractorTest.kt index 9317f798..08a667b9 100644 --- a/tests/unit/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/FetchPreviewsInteractorTest.kt +++ b/tests/unit/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/FetchPreviewsInteractorTest.kt @@ -121,7 +121,7 @@ class FetchPreviewsInteractorTest { @Test fun setsInitialPreviews() = runTestWithDeps { deps -> - backgroundScope.launch { deps.underTest.launch() } + backgroundScope.launch { deps.underTest.activate() } runCurrent() assertThat(deps.previewsRepo.previewsModel.value) @@ -147,7 +147,7 @@ class FetchPreviewsInteractorTest { @Test fun lookupCursorFromContentResolver() = runTestWithDeps { deps -> - backgroundScope.launch { deps.underTest.launch() } + backgroundScope.launch { deps.underTest.activate() } deps.cursorResolver.complete() runCurrent() @@ -173,7 +173,7 @@ class FetchPreviewsInteractorTest { pageSize = 16, maxLoadedPages = 1, ) { deps -> - backgroundScope.launch { deps.underTest.launch() } + backgroundScope.launch { deps.underTest.activate() } deps.cursorResolver.complete() runCurrent() @@ -205,7 +205,7 @@ class FetchPreviewsInteractorTest { pageSize = 16, maxLoadedPages = 2, ) { deps -> - backgroundScope.launch { deps.underTest.launch() } + backgroundScope.launch { deps.underTest.activate() } deps.cursorResolver.complete() runCurrent() @@ -237,7 +237,7 @@ class FetchPreviewsInteractorTest { pageSize = 16, maxLoadedPages = 1, ) { deps -> - backgroundScope.launch { deps.underTest.launch() } + backgroundScope.launch { deps.underTest.activate() } deps.cursorResolver.complete() runCurrent() @@ -268,7 +268,7 @@ class FetchPreviewsInteractorTest { pageSize = 16, maxLoadedPages = 2, ) { deps -> - backgroundScope.launch { deps.underTest.launch() } + backgroundScope.launch { deps.underTest.activate() } deps.cursorResolver.complete() runCurrent() @@ -299,7 +299,7 @@ class FetchPreviewsInteractorTest { pageSize = 16, maxLoadedPages = 2, ) { deps -> - backgroundScope.launch { deps.underTest.launch() } + backgroundScope.launch { deps.underTest.activate() } deps.cursorResolver.complete() runCurrent() @@ -320,7 +320,7 @@ class FetchPreviewsInteractorTest { pageSize = 16, maxLoadedPages = 2, ) { deps -> - backgroundScope.launch { deps.underTest.launch() } + backgroundScope.launch { deps.underTest.activate() } deps.cursorResolver.complete() runCurrent() diff --git a/tests/unit/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/SelectablePreviewInteractorTest.kt b/tests/unit/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/SelectablePreviewInteractorTest.kt index 3dba5329..ff22f37b 100644 --- a/tests/unit/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/SelectablePreviewInteractorTest.kt +++ b/tests/unit/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/SelectablePreviewInteractorTest.kt @@ -18,11 +18,13 @@ package com.android.intentresolver.contentpreview.payloadtoggle.domain.interactor +import android.content.Intent import android.net.Uri -import com.android.intentresolver.contentpreview.payloadtoggle.data.repository.CursorPreviewsRepository +import com.android.intentresolver.contentpreview.payloadtoggle.data.repository.PendingSelectionCallbackRepository import com.android.intentresolver.contentpreview.payloadtoggle.data.repository.PreviewSelectionsRepository import com.android.intentresolver.contentpreview.payloadtoggle.shared.model.PreviewModel -import com.android.intentresolver.contentpreview.payloadtoggle.shared.model.PreviewsModel +import com.android.intentresolver.v2.data.model.fakeChooserRequest +import com.android.intentresolver.v2.data.repository.ChooserRequestRepository import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.first @@ -34,74 +36,113 @@ class SelectablePreviewInteractorTest { @Test fun reflectPreviewRepo_initState() = runTest { - val repo = - CursorPreviewsRepository().apply { - previewsModel.value = - PreviewsModel( - previewModels = - setOf( - PreviewModel( - Uri.fromParts("scheme", "ssp", "fragment"), - "image/bitmap", - ), - PreviewModel( - Uri.fromParts("scheme2", "ssp2", "fragment2"), - "image/bitmap", - ), - ), - startIdx = 0, - loadMoreLeft = null, - loadMoreRight = null, - ) - } val selectionRepo = PreviewSelectionsRepository() + val chooserRequestRepo = + ChooserRequestRepository( + initialRequest = fakeChooserRequest(), + initialActions = emptyList(), + ) + val pendingSelectionCallbackRepo = PendingSelectionCallbackRepository() val underTest = SelectablePreviewInteractor( key = PreviewModel(Uri.fromParts("scheme", "ssp", "fragment"), null), - selectionRepo = selectionRepo, + selectionInteractor = + SelectionInteractor( + selectionsRepo = selectionRepo, + targetIntentModifier = { error("unexpected invocation") }, + updateTargetIntentInteractor = + UpdateTargetIntentInteractor( + repository = pendingSelectionCallbackRepo, + chooserRequestInteractor = + UpdateChooserRequestInteractor( + repository = chooserRequestRepo, + pendingIntentSender = { error("unexpected invocation") }, + ) + ) + ), ) - selectionRepo.setSelection(emptySet()) - testScheduler.runCurrent() + runCurrent() assertThat(underTest.isSelected.first()).isFalse() } @Test fun reflectPreviewRepo_updatedState() = runTest { - val repo = CursorPreviewsRepository() - val selectionRepository = PreviewSelectionsRepository() + val selectionRepo = PreviewSelectionsRepository() + val chooserRequestRepo = + ChooserRequestRepository( + initialRequest = fakeChooserRequest(), + initialActions = emptyList(), + ) + val pendingSelectionCallbackRepo = PendingSelectionCallbackRepository() val underTest = SelectablePreviewInteractor( key = PreviewModel(Uri.fromParts("scheme", "ssp", "fragment"), "image/bitmap"), - selectionRepo = selectionRepository, + selectionInteractor = + SelectionInteractor( + selectionsRepo = selectionRepo, + targetIntentModifier = { error("unexpected invocation") }, + updateTargetIntentInteractor = + UpdateTargetIntentInteractor( + repository = pendingSelectionCallbackRepo, + chooserRequestInteractor = + UpdateChooserRequestInteractor( + repository = chooserRequestRepo, + pendingIntentSender = { error("unexpected invocation") }, + ) + ) + ), ) - selectionRepository.setSelection(emptySet()) assertThat(underTest.isSelected.first()).isFalse() - repo.previewsModel.value = - PreviewsModel( - previewModels = - setOf( - PreviewModel( - Uri.fromParts("scheme", "ssp", "fragment"), - "image/bitmap", - ), - PreviewModel( - Uri.fromParts("scheme2", "ssp2", "fragment2"), - "image/bitmap", - ), + selectionRepo.selections.value = + setOf(PreviewModel(Uri.fromParts("scheme", "ssp", "fragment"), "image/bitmap")) + runCurrent() + + assertThat(underTest.isSelected.first()).isTrue() + } + + @Test + fun setSelected_updatesChooserRequestRepo() = runTest { + val modifiedIntent = Intent() + val selectionRepo = PreviewSelectionsRepository() + val chooserRequestRepo = + ChooserRequestRepository( + initialRequest = fakeChooserRequest(), + initialActions = emptyList(), + ) + val pendingSelectionCallbackRepo = PendingSelectionCallbackRepository() + val underTest = + SelectablePreviewInteractor( + key = PreviewModel(Uri.fromParts("scheme", "ssp", "fragment"), "image/bitmap"), + selectionInteractor = + SelectionInteractor( + selectionsRepo = selectionRepo, + targetIntentModifier = { modifiedIntent }, + updateTargetIntentInteractor = + UpdateTargetIntentInteractor( + repository = pendingSelectionCallbackRepo, + chooserRequestInteractor = + UpdateChooserRequestInteractor( + repository = chooserRequestRepo, + pendingIntentSender = { error("unexpected invocation") }, + ) + ) ), - startIdx = 0, - loadMoreLeft = null, - loadMoreRight = null, ) - selectionRepository.setSelection( - setOf(PreviewModel(Uri.fromParts("scheme", "ssp", "fragment"), "image/bitmap")) - ) + underTest.setSelected(true) runCurrent() - assertThat(underTest.isSelected.first()).isTrue() + assertThat(selectionRepo.selections.value) + .containsExactly( + PreviewModel(Uri.fromParts("scheme", "ssp", "fragment"), "image/bitmap") + ) + + assertThat(chooserRequestRepo.chooserRequest.value.targetIntent) + .isSameInstanceAs(modifiedIntent) + assertThat(pendingSelectionCallbackRepo.pendingTargetIntent.value) + .isSameInstanceAs(modifiedIntent) } } diff --git a/tests/unit/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/SelectablePreviewsInteractorTest.kt b/tests/unit/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/SelectablePreviewsInteractorTest.kt index a5d09f56..3f02c0cd 100644 --- a/tests/unit/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/SelectablePreviewsInteractorTest.kt +++ b/tests/unit/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/SelectablePreviewsInteractorTest.kt @@ -20,9 +20,12 @@ package com.android.intentresolver.contentpreview.payloadtoggle.domain.interacto import android.net.Uri import com.android.intentresolver.contentpreview.payloadtoggle.data.repository.CursorPreviewsRepository +import com.android.intentresolver.contentpreview.payloadtoggle.data.repository.PendingSelectionCallbackRepository import com.android.intentresolver.contentpreview.payloadtoggle.data.repository.PreviewSelectionsRepository import com.android.intentresolver.contentpreview.payloadtoggle.shared.model.PreviewModel import com.android.intentresolver.contentpreview.payloadtoggle.shared.model.PreviewsModel +import com.android.intentresolver.v2.data.model.fakeChooserRequest +import com.android.intentresolver.v2.data.repository.ChooserRequestRepository import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.first @@ -57,16 +60,33 @@ class SelectablePreviewsInteractorTest { } val selectionRepo = PreviewSelectionsRepository().apply { - setSelection( + selections.value = setOf( PreviewModel(Uri.fromParts("scheme", "ssp", "fragment"), null), ) - ) } + val chooserRequestRepo = + ChooserRequestRepository( + initialRequest = fakeChooserRequest(), + initialActions = emptyList(), + ) val underTest = SelectablePreviewsInteractor( previewsRepo = repo, - selectionRepo = selectionRepo, + selectionInteractor = + SelectionInteractor( + selectionRepo, + targetIntentModifier = { error("unexpected invocation") }, + updateTargetIntentInteractor = + UpdateTargetIntentInteractor( + repository = PendingSelectionCallbackRepository(), + chooserRequestInteractor = + UpdateChooserRequestInteractor( + repository = chooserRequestRepo, + pendingIntentSender = { error("unexpected invocation") }, + ) + ) + ), ) val keySet = underTest.previews.stateIn(backgroundScope) @@ -95,9 +115,35 @@ class SelectablePreviewsInteractorTest { val previewsRepo = CursorPreviewsRepository() val selectionRepo = PreviewSelectionsRepository().apply { - setSelection(setOf(PreviewModel(Uri.fromParts("scheme", "ssp", "fragment"), null))) + selections.value = + setOf( + PreviewModel(Uri.fromParts("scheme", "ssp", "fragment"), null), + ) } - val underTest = SelectablePreviewsInteractor(previewsRepo, selectionRepo) + val chooserRequestRepo = + ChooserRequestRepository( + initialRequest = fakeChooserRequest(), + initialActions = emptyList(), + ) + val underTest = + SelectablePreviewsInteractor( + previewsRepo = previewsRepo, + selectionInteractor = + SelectionInteractor( + selectionRepo, + targetIntentModifier = { error("unexpected invocation") }, + updateTargetIntentInteractor = + UpdateTargetIntentInteractor( + repository = PendingSelectionCallbackRepository(), + chooserRequestInteractor = + UpdateChooserRequestInteractor( + repository = chooserRequestRepo, + pendingIntentSender = { error("unexpected invocation") }, + ) + ) + ), + ) + val previews = underTest.previews.stateIn(backgroundScope) val firstModel = underTest.preview(PreviewModel(Uri.fromParts("scheme", "ssp", "fragment"), null)) @@ -124,7 +170,7 @@ class SelectablePreviewsInteractorTest { loadMoreLeft = null, loadMoreRight = { loadRequested = true }, ) - selectionRepo.setSelection(emptySet()) + selectionRepo.selections.value = emptySet() runCurrent() assertThat(previews.value).isNotNull() diff --git a/tests/unit/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/UpdateChooserRequestInteractorTest.kt b/tests/unit/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/UpdateChooserRequestInteractorTest.kt new file mode 100644 index 00000000..05c7646a --- /dev/null +++ b/tests/unit/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/UpdateChooserRequestInteractorTest.kt @@ -0,0 +1,74 @@ +/* + * 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. + */ + +@file:OptIn(ExperimentalCoroutinesApi::class) + +package com.android.intentresolver.contentpreview.payloadtoggle.domain.interactor + +import android.content.Intent +import com.android.intentresolver.contentpreview.payloadtoggle.data.repository.PendingSelectionCallbackRepository +import com.android.intentresolver.contentpreview.payloadtoggle.domain.intent.PendingIntentSender +import com.android.intentresolver.contentpreview.payloadtoggle.domain.model.ShareouselUpdate +import com.android.intentresolver.contentpreview.payloadtoggle.domain.model.ValueUpdate +import com.android.intentresolver.v2.data.model.fakeChooserRequest +import com.android.intentresolver.v2.data.repository.ChooserRequestRepository +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.launch +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Test + +class UpdateChooserRequestInteractorTest { + @Test + fun updateTargetIntentWithSelection() = runTest { + val pendingIntentSender = PendingIntentSender {} + val chooserRequestRepository = + ChooserRequestRepository( + initialRequest = fakeChooserRequest(), + initialActions = emptyList(), + ) + val selectionCallbackResult = ShareouselUpdate(metadataText = ValueUpdate.Value("update")) + val pendingSelectionCallbackRepository = PendingSelectionCallbackRepository() + val updateTargetIntentInteractor = + UpdateTargetIntentInteractor( + repository = pendingSelectionCallbackRepository, + chooserRequestInteractor = + UpdateChooserRequestInteractor( + repository = chooserRequestRepository, + pendingIntentSender = pendingIntentSender, + ) + ) + val processTargetIntentUpdatesInteractor = + ProcessTargetIntentUpdatesInteractor( + selectionCallback = { selectionCallbackResult }, + repository = pendingSelectionCallbackRepository, + chooserRequestInteractor = + UpdateChooserRequestInteractor( + repository = chooserRequestRepository, + pendingIntentSender = pendingIntentSender, + ) + ) + + backgroundScope.launch { processTargetIntentUpdatesInteractor.activate() } + + updateTargetIntentInteractor.updateTargetIntent(Intent()) + runCurrent() + + assertThat(pendingSelectionCallbackRepository.pendingTargetIntent.value).isNull() + assertThat(chooserRequestRepository.chooserRequest.value.metadataText).isEqualTo("update") + } +} diff --git a/tests/unit/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/UpdateTargetIntentInteractorTest.kt b/tests/unit/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/UpdateTargetIntentInteractorTest.kt deleted file mode 100644 index 3f437b22..00000000 --- a/tests/unit/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/UpdateTargetIntentInteractorTest.kt +++ /dev/null @@ -1,98 +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. - */ - -@file:OptIn(ExperimentalCoroutinesApi::class) - -package com.android.intentresolver.contentpreview.payloadtoggle.domain.interactor - -import android.content.Intent -import android.net.Uri -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.model.ShareouselUpdate -import com.android.intentresolver.contentpreview.payloadtoggle.shared.model.PreviewModel -import com.google.common.truth.Truth.assertThat -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.flow.filterNotNull -import kotlinx.coroutines.flow.first -import kotlinx.coroutines.launch -import kotlinx.coroutines.test.runCurrent -import kotlinx.coroutines.test.runTest -import org.junit.Test - -class UpdateTargetIntentInteractorTest { - @Test - fun updateTargetIntentWithSelection() = runTest { - val initialIntent = Intent() - val intentRepository = TargetIntentRepository(initialIntent, emptyList()) - val selectionRepository = PreviewSelectionsRepository() - val chooserParamsUpdateRepository = ChooserParamsUpdateRepository() - val selectionCallbackResult = ShareouselUpdate() - val underTest = - UpdateTargetIntentInteractor( - intentRepository = intentRepository, - chooserParamsUpdateRepository = chooserParamsUpdateRepository, - selectionCallback = { selectionCallbackResult }, - selectionRepo = selectionRepository, - targetIntentModifier = { selection -> - Intent() - .putParcelableArrayListExtra( - "selection", - selection.mapTo(ArrayList()) { it.uri }, - ) - }, - pendingIntentSender = {}, - ) - - backgroundScope.launch { underTest.launch() } - selectionRepository.setSelection( - setOf( - PreviewModel(Uri.fromParts("scheme0", "ssp0", "fragment0"), null), - PreviewModel(Uri.fromParts("scheme1", "ssp1", "fragment1"), null), - ) - ) - runCurrent() - - // only changes in selection should trigger intent updates - assertThat( - intentRepository.targetIntent.value.intent.getParcelableArrayListExtra( - "selection", - Uri::class.java, - ) - ) - .isNull() - - selectionRepository.select( - PreviewModel(Uri.fromParts("scheme2", "ssp2", "fragment2"), null), - ) - runCurrent() - - assertThat( - intentRepository.targetIntent.value.intent.getParcelableArrayListExtra( - "selection", - Uri::class.java, - ) - ) - .containsExactly( - Uri.fromParts("scheme0", "ssp0", "fragment0"), - Uri.fromParts("scheme1", "ssp1", "fragment1"), - Uri.fromParts("scheme2", "ssp2", "fragment2"), - ) - assertThat(chooserParamsUpdateRepository.updates.filterNotNull().first()) - .isEqualTo(selectionCallbackResult) - } -} diff --git a/tests/unit/src/com/android/intentresolver/contentpreview/payloadtoggle/ui/viewmodel/ShareouselViewModelTest.kt b/tests/unit/src/com/android/intentresolver/contentpreview/payloadtoggle/ui/viewmodel/ShareouselViewModelTest.kt index 24035c54..5d95df04 100644 --- a/tests/unit/src/com/android/intentresolver/contentpreview/payloadtoggle/ui/viewmodel/ShareouselViewModelTest.kt +++ b/tests/unit/src/com/android/intentresolver/contentpreview/payloadtoggle/ui/viewmodel/ShareouselViewModelTest.kt @@ -30,17 +30,24 @@ import com.android.intentresolver.contentpreview.HeadlineGenerator import com.android.intentresolver.contentpreview.payloadtoggle.data.model.CustomActionModel import com.android.intentresolver.contentpreview.payloadtoggle.data.repository.ActivityResultRepository import com.android.intentresolver.contentpreview.payloadtoggle.data.repository.CursorPreviewsRepository +import com.android.intentresolver.contentpreview.payloadtoggle.data.repository.PendingSelectionCallbackRepository 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.PendingIntentSender +import com.android.intentresolver.contentpreview.payloadtoggle.domain.intent.TargetIntentModifier +import com.android.intentresolver.contentpreview.payloadtoggle.domain.interactor.ChooserRequestInteractor import com.android.intentresolver.contentpreview.payloadtoggle.domain.interactor.CustomActionsInteractor import com.android.intentresolver.contentpreview.payloadtoggle.domain.interactor.SelectablePreviewsInteractor import com.android.intentresolver.contentpreview.payloadtoggle.domain.interactor.SelectionInteractor +import com.android.intentresolver.contentpreview.payloadtoggle.domain.interactor.UpdateChooserRequestInteractor +import com.android.intentresolver.contentpreview.payloadtoggle.domain.interactor.UpdateTargetIntentInteractor import com.android.intentresolver.contentpreview.payloadtoggle.shared.model.PreviewModel import com.android.intentresolver.contentpreview.payloadtoggle.shared.model.PreviewsModel import com.android.intentresolver.icon.BitmapIcon import com.android.intentresolver.logging.FakeEventLog import com.android.intentresolver.mock import com.android.intentresolver.util.comparingElementsUsingTransform +import com.android.intentresolver.v2.data.model.fakeChooserRequest +import com.android.intentresolver.v2.data.repository.ChooserRequestRepository import com.android.internal.logging.InstanceId import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertWithMessage @@ -54,30 +61,75 @@ import org.junit.Test class ShareouselViewModelTest { - class Dependencies { + class Dependencies( + val pendingIntentSender: PendingIntentSender, + val targetIntentModifier: TargetIntentModifier<PreviewModel>, + ) { val testDispatcher = StandardTestDispatcher() val testScope = TestScope(testDispatcher) val previewsRepository = CursorPreviewsRepository() val selectionRepository = PreviewSelectionsRepository().apply { - setSelection(setOf(PreviewModel(Uri.fromParts("scheme", "ssp", "fragment"), null))) + selections.value = + setOf(PreviewModel(Uri.fromParts("scheme", "ssp", "fragment"), null)) } val activityResultRepository = ActivityResultRepository() val contentResolver = mock<ContentResolver> {} val packageManager = mock<PackageManager> {} val eventLog = FakeEventLog(instanceId = InstanceId.fakeInstanceId(1)) - val targetIntentRepo = - TargetIntentRepository( - initialIntent = Intent(), - initialActions = listOf(), + val chooserRequestRepo = + ChooserRequestRepository( + initialRequest = fakeChooserRequest(), + initialActions = emptyList(), ) + val pendingSelectionCallbackRepo = PendingSelectionCallbackRepository() + + val actionsInteractor + get() = + CustomActionsInteractor( + activityResultRepo = activityResultRepository, + bgDispatcher = testDispatcher, + contentResolver = contentResolver, + eventLog = eventLog, + packageManager = packageManager, + chooserRequestInteractor = chooserRequestInteractor, + ) + + val selectionInteractor + get() = + SelectionInteractor( + selectionsRepo = selectionRepository, + targetIntentModifier = targetIntentModifier, + updateTargetIntentInteractor = updateTargetIntentInteractor, + ) + + val updateTargetIntentInteractor + get() = + UpdateTargetIntentInteractor( + repository = pendingSelectionCallbackRepo, + chooserRequestInteractor = updateChooserRequestInteractor, + ) + + val updateChooserRequestInteractor + get() = + UpdateChooserRequestInteractor( + repository = chooserRequestRepo, + pendingIntentSender = pendingIntentSender, + ) + + val chooserRequestInteractor + get() = ChooserRequestInteractor(repository = chooserRequestRepo) + + val previewsInteractor + get() = + SelectablePreviewsInteractor( + previewsRepo = previewsRepository, + selectionInteractor = selectionInteractor, + ) + val underTest = ShareouselViewModelModule.create( - interactor = - SelectablePreviewsInteractor( - previewsRepo = previewsRepository, - selectionRepo = selectionRepository - ), + interactor = previewsInteractor, imageLoader = FakeImageLoader( initialBitmaps = @@ -86,15 +138,7 @@ class ShareouselViewModelTest { Bitmap.createBitmap(100, 100, Bitmap.Config.ALPHA_8) ) ), - actionsInteractor = - CustomActionsInteractor( - activityResultRepo = activityResultRepository, - bgDispatcher = testDispatcher, - contentResolver = contentResolver, - eventLog = eventLog, - packageManager = packageManager, - targetIntentRepo = targetIntentRepo, - ), + actionsInteractor = actionsInteractor, headlineGenerator = object : HeadlineGenerator { override fun getImagesHeadline(count: Int): String = "IMAGES: $count" @@ -123,18 +167,19 @@ class ShareouselViewModelTest { override fun getFilesHeadline(count: Int): String = error("not supported") }, - selectionInteractor = - SelectionInteractor( - selectionRepo = selectionRepository, - ), + selectionInteractor = selectionInteractor, scope = testScope.backgroundScope, ) } private inline fun runTestWithDeps( + pendingIntentSender: PendingIntentSender = PendingIntentSender {}, + targetIntentModifier: TargetIntentModifier<PreviewModel> = TargetIntentModifier { + error("unexpected invocation") + }, crossinline block: suspend TestScope.(Dependencies) -> Unit, ): Unit = - Dependencies().run { + Dependencies(pendingIntentSender, targetIntentModifier).run { testScope.runTest { runCurrent() block(this@run) @@ -145,7 +190,7 @@ class ShareouselViewModelTest { fun headline() = runTestWithDeps { deps -> with(deps) { assertThat(underTest.headline.first()).isEqualTo("IMAGES: 1") - selectionRepository.setSelection( + selectionRepository.selections.value = setOf( PreviewModel( Uri.fromParts("scheme", "ssp", "fragment"), @@ -156,88 +201,105 @@ class ShareouselViewModelTest { null, ) ) - ) runCurrent() assertThat(underTest.headline.first()).isEqualTo("IMAGES: 2") } } @Test - fun previews() = runTestWithDeps { deps -> - with(deps) { - previewsRepository.previewsModel.value = - PreviewsModel( - previewModels = - setOf( - PreviewModel( - Uri.fromParts("scheme", "ssp", "fragment"), - null, + fun previews() = + runTestWithDeps(targetIntentModifier = { Intent() }) { deps -> + with(deps) { + previewsRepository.previewsModel.value = + PreviewsModel( + previewModels = + setOf( + PreviewModel( + Uri.fromParts("scheme", "ssp", "fragment"), + null, + ), + PreviewModel( + Uri.fromParts("scheme1", "ssp1", "fragment1"), + null, + ) ), - PreviewModel( - Uri.fromParts("scheme1", "ssp1", "fragment1"), - null, - ) - ), - startIdx = 1, - loadMoreLeft = null, - loadMoreRight = null, - ) - runCurrent() + startIdx = 1, + loadMoreLeft = null, + loadMoreRight = null, + ) + runCurrent() - assertWithMessage("previewsKeys is null").that(underTest.previews.first()).isNotNull() - assertThat(underTest.previews.first()!!.previewModels) - .comparingElementsUsingTransform("has uri of") { it: PreviewModel -> it.uri } - .containsExactly( - Uri.fromParts("scheme", "ssp", "fragment"), - Uri.fromParts("scheme1", "ssp1", "fragment1"), - ) - .inOrder() + assertWithMessage("previewsKeys is null") + .that(underTest.previews.first()) + .isNotNull() + assertThat(underTest.previews.first()!!.previewModels) + .comparingElementsUsingTransform("has uri of") { it: PreviewModel -> it.uri } + .containsExactly( + Uri.fromParts("scheme", "ssp", "fragment"), + Uri.fromParts("scheme1", "ssp1", "fragment1"), + ) + .inOrder() - val previewVm = - underTest.preview(PreviewModel(Uri.fromParts("scheme1", "ssp1", "fragment1"), null)) + val previewVm = + underTest.preview( + PreviewModel(Uri.fromParts("scheme1", "ssp1", "fragment1"), null) + ) - assertWithMessage("preview bitmap is null").that(previewVm.bitmap.first()).isNotNull() - assertThat(previewVm.isSelected.first()).isFalse() + assertWithMessage("preview bitmap is null") + .that(previewVm.bitmap.first()) + .isNotNull() + assertThat(previewVm.isSelected.first()).isFalse() - previewVm.setSelected(true) + previewVm.setSelected(true) - assertThat(selectionRepository.selections.first().selection) - .comparingElementsUsingTransform("has uri of") { model: PreviewModel -> model.uri } - .contains(Uri.fromParts("scheme1", "ssp1", "fragment1")) + assertThat(selectionRepository.selections.value) + .comparingElementsUsingTransform("has uri of") { model: PreviewModel -> + model.uri + } + .contains(Uri.fromParts("scheme1", "ssp1", "fragment1")) + } } - } @Test - fun actions() = runTestWithDeps { deps -> - with(deps) { - assertThat(underTest.actions.first()).isEmpty() + fun actions() { + runTestWithDeps { deps -> + with(deps) { + assertThat(underTest.actions.first()).isEmpty() - val bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ALPHA_8) - val icon = Icon.createWithBitmap(bitmap) - var actionSent = false - targetIntentRepo.customActions.value = - listOf(CustomActionModel("label1", icon) { actionSent = true }) - runCurrent() + val bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ALPHA_8) + val icon = Icon.createWithBitmap(bitmap) + var actionSent = false + chooserRequestRepo.customActions.value = + listOf( + CustomActionModel( + label = "label1", + icon = icon, + performAction = { actionSent = true }, + ) + ) + runCurrent() + + assertThat(underTest.actions.first()) + .comparingElementsUsingTransform("has a label of") { vm: ActionChipViewModel -> + vm.label + } + .containsExactly("label1") + .inOrder() + assertThat(underTest.actions.first()) + .comparingElementsUsingTransform("has an icon of") { vm: ActionChipViewModel -> + vm.icon + } + .containsExactly(BitmapIcon(icon.bitmap)) + .inOrder() - assertThat(underTest.actions.first()) - .comparingElementsUsingTransform("has a label of") { vm: ActionChipViewModel -> - vm.label - } - .containsExactly("label1") - .inOrder() - assertThat(underTest.actions.first()) - .comparingElementsUsingTransform("has an icon of") { vm: ActionChipViewModel -> - vm.icon - } - .containsExactly(BitmapIcon(icon.bitmap)) - .inOrder() - - underTest.actions.first()[0].onClicked() - - assertThat(actionSent).isTrue() - assertThat(eventLog.customActionSelected) - .isEqualTo(FakeEventLog.CustomActionSelected(0)) - assertThat(activityResultRepository.activityResult.value).isEqualTo(Activity.RESULT_OK) + underTest.actions.first()[0].onClicked() + + assertThat(actionSent).isTrue() + assertThat(eventLog.customActionSelected) + .isEqualTo(FakeEventLog.CustomActionSelected(0)) + assertThat(activityResultRepository.activityResult.value) + .isEqualTo(Activity.RESULT_OK) + } } } } diff --git a/java/src/com/android/intentresolver/contentpreview/payloadtoggle/data/model/TargetIntentRecord.kt b/tests/unit/src/com/android/intentresolver/v2/data/model/FakeChooserRequest.kt index 17393023..559e3b77 100644 --- a/java/src/com/android/intentresolver/contentpreview/payloadtoggle/data/model/TargetIntentRecord.kt +++ b/tests/unit/src/com/android/intentresolver/v2/data/model/FakeChooserRequest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2024 The Android Open Source Project + * 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. @@ -14,8 +14,13 @@ * limitations under the License. */ -package com.android.intentresolver.contentpreview.payloadtoggle.data.model +package com.android.intentresolver.v2.data.model import android.content.Intent +import android.net.Uri -data class TargetIntentRecord(val isInitial: Boolean, val intent: Intent) +fun fakeChooserRequest( + intent: Intent = Intent(), + packageName: String = "pkg", + referrer: Uri? = null, +) = ChooserRequest(intent, packageName, null) diff --git a/tests/unit/src/com/android/intentresolver/v2/domain/interactor/ChooserRequestUpdateInteractorTest.kt b/tests/unit/src/com/android/intentresolver/v2/domain/interactor/ChooserRequestUpdateInteractorTest.kt deleted file mode 100644 index d05a0a91..00000000 --- a/tests/unit/src/com/android/intentresolver/v2/domain/interactor/ChooserRequestUpdateInteractorTest.kt +++ /dev/null @@ -1,204 +0,0 @@ -/* - * 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.v2.domain.interactor - -import android.content.Intent -import android.content.Intent.ACTION_SEND -import android.content.Intent.ACTION_SEND_MULTIPLE -import android.content.Intent.EXTRA_STREAM -import android.content.IntentSender -import android.net.Uri -import com.android.intentresolver.contentpreview.payloadtoggle.data.repository.ChooserParamsUpdateRepository -import com.android.intentresolver.contentpreview.payloadtoggle.data.repository.TargetIntentRepository -import com.android.intentresolver.contentpreview.payloadtoggle.domain.model.ShareouselUpdate -import com.android.intentresolver.contentpreview.payloadtoggle.domain.model.ValueUpdate -import com.android.intentresolver.mock -import com.android.intentresolver.v2.ui.model.ChooserRequest -import com.google.common.truth.Truth.assertThat -import com.google.common.truth.Truth.assertWithMessage -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.launch -import kotlinx.coroutines.test.TestScope -import kotlinx.coroutines.test.runTest -import org.junit.Test - -class ChooserRequestUpdateInteractorTest { - private val targetIntent = - Intent(ACTION_SEND_MULTIPLE).apply { - putExtra( - EXTRA_STREAM, - ArrayList<Uri>().apply { - add(createUri(1)) - add(createUri(2)) - } - ) - type = "image/png" - } - val initialRequest = createSomeChooserRequest(targetIntent) - private val targetIntentRepository = - TargetIntentRepository( - targetIntent, - emptyList(), - ) - private val chooserParamsUpdateRepository = ChooserParamsUpdateRepository() - private val testScope = TestScope() - - @Test - fun testInitialIntentOnly_noUpdates() = - testScope.runTest { - val requestFlow = MutableStateFlow(initialRequest) - val testSubject = - ChooserRequestUpdateInteractor( - targetIntentRepository, - chooserParamsUpdateRepository, - requestFlow, - ) - backgroundScope.launch { testSubject.launch() } - testScheduler.runCurrent() - - assertWithMessage("No updates expected") - .that(requestFlow.value) - .isSameInstanceAs(initialRequest) - } - - @Test - fun testIntentUpdate_newRequestPublished() = - testScope.runTest { - val requestFlow = MutableStateFlow(initialRequest) - val testSubject = - ChooserRequestUpdateInteractor( - targetIntentRepository, - chooserParamsUpdateRepository, - requestFlow, - ) - backgroundScope.launch { testSubject.launch() } - targetIntentRepository.updateTargetIntent( - Intent(targetIntent).apply { - action = ACTION_SEND - putExtra(EXTRA_STREAM, createUri(2)) - } - ) - testScheduler.runCurrent() - - assertWithMessage("Another chooser request is expected") - .that(requestFlow.value) - .isNotEqualTo(initialRequest) - } - - @Test - fun testChooserParamsUpdate_newRequestPublished() = - testScope.runTest { - val requestFlow = MutableStateFlow(initialRequest) - val testSubject = - ChooserRequestUpdateInteractor( - targetIntentRepository, - chooserParamsUpdateRepository, - requestFlow, - ) - backgroundScope.launch { testSubject.launch() } - val newResultSender = mock<IntentSender>() - chooserParamsUpdateRepository.setUpdates( - ShareouselUpdate( - resultIntentSender = ValueUpdate.Value(newResultSender), - ) - ) - testScheduler.runCurrent() - - assertWithMessage("Another chooser request is expected") - .that(requestFlow.value) - .isNotEqualTo(initialRequest) - - assertWithMessage("Another chooser request is expected") - .that(requestFlow.value.chosenComponentSender) - .isSameInstanceAs(newResultSender) - } - - @Test - fun testTargetIntentUpdateDoesNotOverrideOtherParameters() = - testScope.runTest { - val requestFlow = MutableStateFlow(initialRequest) - val testSubject = - ChooserRequestUpdateInteractor( - targetIntentRepository, - chooserParamsUpdateRepository, - requestFlow, - ) - backgroundScope.launch { testSubject.launch() } - - val newResultSender = mock<IntentSender>() - val newTargetIntent = Intent(Intent.ACTION_VIEW) - chooserParamsUpdateRepository.setUpdates( - ShareouselUpdate( - resultIntentSender = ValueUpdate.Value(newResultSender), - ) - ) - testScheduler.runCurrent() - targetIntentRepository.updateTargetIntent(newTargetIntent) - testScheduler.runCurrent() - - assertThat(requestFlow.value.targetIntent).isSameInstanceAs(newTargetIntent) - - assertThat(requestFlow.value.chosenComponentSender).isSameInstanceAs(newResultSender) - } - - @Test - fun testUpdateWithNullValues() = - testScope.runTest { - val initialRequest = - ChooserRequest( - targetIntent = targetIntent, - targetAction = targetIntent.action, - isSendActionTarget = true, - targetType = null, - launchedFromPackage = "", - referrer = null, - refinementIntentSender = mock<IntentSender>(), - chosenComponentSender = mock<IntentSender>(), - ) - val requestFlow = MutableStateFlow(initialRequest) - val testSubject = - ChooserRequestUpdateInteractor( - targetIntentRepository, - chooserParamsUpdateRepository, - requestFlow, - ) - backgroundScope.launch { testSubject.launch() } - - chooserParamsUpdateRepository.setUpdates( - ShareouselUpdate( - resultIntentSender = ValueUpdate.Value(null), - refinementIntentSender = ValueUpdate.Value(null), - ) - ) - testScheduler.runCurrent() - - assertThat(requestFlow.value.chosenComponentSender).isNull() - assertThat(requestFlow.value.refinementIntentSender).isNull() - } -} - -private fun createSomeChooserRequest(targetIntent: Intent) = - ChooserRequest( - targetIntent = targetIntent, - targetAction = targetIntent.action, - isSendActionTarget = true, - targetType = null, - launchedFromPackage = "", - referrer = null, - ) - -private fun createUri(id: Int) = Uri.parse("content://org.pkg.app/image-$id.png") diff --git a/tests/unit/src/com/android/intentresolver/v2/ui/viewmodel/ChooserRequestTest.kt b/tests/unit/src/com/android/intentresolver/v2/ui/viewmodel/ChooserRequestTest.kt index d3b9f559..987d55fc 100644 --- a/tests/unit/src/com/android/intentresolver/v2/ui/viewmodel/ChooserRequestTest.kt +++ b/tests/unit/src/com/android/intentresolver/v2/ui/viewmodel/ChooserRequestTest.kt @@ -31,8 +31,8 @@ import androidx.core.net.toUri import androidx.core.os.bundleOf import com.android.intentresolver.ContentTypeHint import com.android.intentresolver.inject.FakeChooserServiceFlags +import com.android.intentresolver.v2.data.model.ChooserRequest import com.android.intentresolver.v2.ui.model.ActivityModel -import com.android.intentresolver.v2.ui.model.ChooserRequest import com.android.intentresolver.v2.validation.Importance import com.android.intentresolver.v2.validation.Invalid import com.android.intentresolver.v2.validation.NoValue @@ -126,10 +126,7 @@ class ChooserRequestTest { fun payloadIntents_includesTargetThenAdditional() { val intent1 = Intent(ACTION_SEND) val intent2 = Intent(ACTION_SEND_MULTIPLE) - val model = createActivityModel( - targetIntent = intent1, - additionalIntents = listOf(intent2) - ) + val model = createActivityModel(targetIntent = intent1, additionalIntents = listOf(intent2)) val result = readChooserRequest(model, fakeChooserServiceFlags) @@ -229,7 +226,8 @@ class ChooserRequestTest { fakeChooserServiceFlags.setFlag(Flags.FLAG_CHOOSER_PAYLOAD_TOGGLING, true) val uri = Uri.parse("content://org.pkg/path") val position = 10 - val model = createActivityModel(targetIntent = Intent(ACTION_VIEW)).apply { + val model = + createActivityModel(targetIntent = Intent(ACTION_VIEW)).apply { intent.putExtra(EXTRA_CHOOSER_ADDITIONAL_CONTENT_URI, uri) intent.putExtra(EXTRA_CHOOSER_FOCUSED_ITEM_POSITION, position) } @@ -266,7 +264,8 @@ class ChooserRequestTest { fun metadataText_whenFlagFalse_isNull() { fakeChooserServiceFlags.setFlag(Flags.FLAG_ENABLE_SHARESHEET_METADATA_EXTRA, false) val metadataText: CharSequence = "Test metadata text" - val model = createActivityModel(targetIntent = Intent()).apply { + val model = + createActivityModel(targetIntent = Intent()).apply { intent.putExtra(Intent.EXTRA_METADATA_TEXT, metadataText) } @@ -283,7 +282,8 @@ class ChooserRequestTest { // Arrange fakeChooserServiceFlags.setFlag(Flags.FLAG_ENABLE_SHARESHEET_METADATA_EXTRA, true) val metadataText: CharSequence = "Test metadata text" - val model = createActivityModel(targetIntent = Intent()).apply { + val model = + createActivityModel(targetIntent = Intent()).apply { intent.putExtra(Intent.EXTRA_METADATA_TEXT, metadataText) } diff --git a/tests/unit/src/com/android/intentresolver/v2/ui/viewmodel/ResolverRequestTest.kt b/tests/unit/src/com/android/intentresolver/v2/ui/viewmodel/ResolverRequestTest.kt index 6f1ed853..f6475663 100644 --- a/tests/unit/src/com/android/intentresolver/v2/ui/viewmodel/ResolverRequestTest.kt +++ b/tests/unit/src/com/android/intentresolver/v2/ui/viewmodel/ResolverRequestTest.kt @@ -24,7 +24,6 @@ import androidx.core.os.bundleOf import com.android.intentresolver.v2.ResolverActivity.PROFILE_WORK import com.android.intentresolver.v2.shared.model.Profile.Type.WORK import com.android.intentresolver.v2.ui.model.ActivityModel -import com.android.intentresolver.v2.ui.model.ChooserRequest import com.android.intentresolver.v2.ui.model.ResolverRequest import com.android.intentresolver.v2.validation.Invalid import com.android.intentresolver.v2.validation.UncaughtException |