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
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 c8fcb9d..0000000
--- 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/ChooserParamsUpdateRepository.kt
deleted file mode 100644
index 1a4f2b8..0000000
--- a/java/src/com/android/intentresolver/contentpreview/payloadtoggle/data/repository/ChooserParamsUpdateRepository.kt
+++ /dev/null
@@ -1,34 +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 com.android.intentresolver.contentpreview.payloadtoggle.domain.model.ShareouselUpdate
-import dagger.hilt.android.scopes.ViewModelScoped
-import javax.inject.Inject
-import kotlinx.coroutines.flow.MutableStateFlow
-
-/** Chooser parameters Updates received from the sharing application payload change callback */
-// TODO: a scaffolding repository to deliver chooser parameter updates before we developed some
-// other, more thought-through solution.
-@ViewModelScoped
-class ChooserParamsUpdateRepository @Inject constructor() {
- val updates = MutableStateFlow<ShareouselUpdate?>(null)
-
- fun setUpdates(update: ShareouselUpdate) {
- updates.tryEmit(update)
- }
-}
diff --git a/java/src/com/android/intentresolver/contentpreview/payloadtoggle/data/repository/PendingSelectionCallbackRepository.kt b/java/src/com/android/intentresolver/contentpreview/payloadtoggle/data/repository/PendingSelectionCallbackRepository.kt
new file mode 100644
index 0000000..1745cd9
--- /dev/null
+++ b/java/src/com/android/intentresolver/contentpreview/payloadtoggle/data/repository/PendingSelectionCallbackRepository.kt
@@ -0,0 +1,32 @@
+/*
+ * 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.data.repository
+
+import android.content.Intent
+import dagger.hilt.android.scopes.ActivityRetainedScoped
+import javax.inject.Inject
+import kotlinx.coroutines.flow.MutableStateFlow
+
+/** 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 b461d10..9aecc98 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/data/repository/TargetIntentRepository.kt b/java/src/com/android/intentresolver/contentpreview/payloadtoggle/data/repository/TargetIntentRepository.kt
deleted file mode 100644
index bb43323..0000000
--- a/java/src/com/android/intentresolver/contentpreview/payloadtoggle/data/repository/TargetIntentRepository.kt
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.intentresolver.contentpreview.payloadtoggle.data.repository
-
-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 javax.inject.Inject
-import kotlinx.coroutines.flow.MutableStateFlow
-
-/** Stores the target intent of the share sheet, and custom actions derived from the intent. */
-@ViewModelScoped
-class TargetIntentRepository
-@Inject
-constructor(
- @TargetIntent initialIntent: Intent,
- initialActions: List<CustomActionModel>,
-) {
- val targetIntent = MutableStateFlow(TargetIntentRecord(isInitial = true, initialIntent))
-
- // 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)
- }
-}
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 577dc34..4a2a693 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 @@
/** 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 @@
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/domain/interactor/ChooserRequestInteractor.kt b/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/ChooserRequestInteractor.kt
new file mode 100644
index 0000000..61c04ac
--- /dev/null
+++ b/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/ChooserRequestInteractor.kt
@@ -0,0 +1,38 @@
+/*
+ * 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.data.model.CustomActionModel
+import com.android.intentresolver.v2.data.repository.ChooserRequestRepository
+import javax.inject.Inject
+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. */
+class ChooserRequestInteractor
+@Inject
+constructor(
+ private val repository: ChooserRequestRepository,
+) {
+ val targetIntent: Flow<Intent>
+ get() = repository.chooserRequest.map { it.targetIntent }
+
+ 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 56f781f..e973e84 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.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 @@
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 a7749c9..9bc7ae6 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 @@
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 0000000..04416a3
--- /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 3b5b0dd..55a995f 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 @@
/** 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 78e208f..a578d0e 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 @@
@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 @@
* 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 0b8bcdd..a570f36 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 0000000..9e48cd2
--- /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 06e28cb..429e34e 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 c08c7f4..ff2bb14 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.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 @@
@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 ffa0469..d624c9e 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.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.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 f2a2726..503e46d 100644
--- a/java/src/com/android/intentresolver/v2/ChooserHelper.kt
+++ b/java/src/com/android/intentresolver/v2/ChooserHelper.kt
@@ -29,9 +29,9 @@
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
similarity index 99%
rename from java/src/com/android/intentresolver/v2/ui/model/ChooserRequest.kt
rename to java/src/com/android/intentresolver/v2/data/model/ChooserRequest.kt
index 4f3cf3c..7c9c861 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 0000000..d23e07e
--- /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 3721340..0000000
--- 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 07b1743..67c2a25 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.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 7ebf65a..a25fcbe 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.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 4431a54..e39329b 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.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 @@
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 @@
"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 48c8b58..0000000
--- 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 f4be47e..7c36ef5 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 @@
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 @@
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 95ad966..ceb20da 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 @@
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 @@
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 @@
@Test
fun customActions_tracksRepoUpdates() =
runTest(testDispatcher) {
- val targetIntentRepository =
- TargetIntentRepository(
- initialIntent = Intent(),
+ val chooserRequestRepository =
+ ChooserRequestRepository(
+ initialRequest = fakeChooserRequest(),
initialActions = emptyList(),
)
val underTest =
@@ -92,7 +96,8 @@
contentResolver = mock {},
eventLog = mock {},
packageManager = mock {},
- targetIntentRepo = targetIntentRepository,
+ chooserRequestInteractor =
+ ChooserRequestInteractor(repository = chooserRequestRepository),
)
val customActions: StateFlow<List<ActionModel>> =
@@ -100,7 +105,7 @@
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 @@
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 @@
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 9317f79..08a667b 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 @@
@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 @@
@Test
fun lookupCursorFromContentResolver() = runTestWithDeps { deps ->
- backgroundScope.launch { deps.underTest.launch() }
+ backgroundScope.launch { deps.underTest.activate() }
deps.cursorResolver.complete()
runCurrent()
@@ -173,7 +173,7 @@
pageSize = 16,
maxLoadedPages = 1,
) { deps ->
- backgroundScope.launch { deps.underTest.launch() }
+ backgroundScope.launch { deps.underTest.activate() }
deps.cursorResolver.complete()
runCurrent()
@@ -205,7 +205,7 @@
pageSize = 16,
maxLoadedPages = 2,
) { deps ->
- backgroundScope.launch { deps.underTest.launch() }
+ backgroundScope.launch { deps.underTest.activate() }
deps.cursorResolver.complete()
runCurrent()
@@ -237,7 +237,7 @@
pageSize = 16,
maxLoadedPages = 1,
) { deps ->
- backgroundScope.launch { deps.underTest.launch() }
+ backgroundScope.launch { deps.underTest.activate() }
deps.cursorResolver.complete()
runCurrent()
@@ -268,7 +268,7 @@
pageSize = 16,
maxLoadedPages = 2,
) { deps ->
- backgroundScope.launch { deps.underTest.launch() }
+ backgroundScope.launch { deps.underTest.activate() }
deps.cursorResolver.complete()
runCurrent()
@@ -299,7 +299,7 @@
pageSize = 16,
maxLoadedPages = 2,
) { deps ->
- backgroundScope.launch { deps.underTest.launch() }
+ backgroundScope.launch { deps.underTest.activate() }
deps.cursorResolver.complete()
runCurrent()
@@ -320,7 +320,7 @@
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 3dba532..ff22f37 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 @@
@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",
- ),
- ),
- startIdx = 0,
- loadMoreLeft = null,
- loadMoreRight = null,
- )
-
- selectionRepository.setSelection(
+ 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") },
+ )
+ )
+ ),
+ )
+
+ underTest.setSelected(true)
+ runCurrent()
+
+ 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 a5d09f5..3f02c0c 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 @@
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 @@
}
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 @@
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 @@
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 0000000..05c7646
--- /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 3f437b2..0000000
--- 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 24035c5..5d95df0 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.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 @@
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 @@
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 @@
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 @@
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 @@
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()
+ underTest.actions.first()[0].onClicked()
- assertThat(actionSent).isTrue()
- assertThat(eventLog.customActionSelected)
- .isEqualTo(FakeEventLog.CustomActionSelected(0))
- assertThat(activityResultRepository.activityResult.value).isEqualTo(Activity.RESULT_OK)
+ 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
similarity index 67%
rename from java/src/com/android/intentresolver/contentpreview/payloadtoggle/data/model/TargetIntentRecord.kt
rename to tests/unit/src/com/android/intentresolver/v2/data/model/FakeChooserRequest.kt
index 1739302..559e3b7 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 d05a0a9..0000000
--- 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 d3b9f55..987d55f 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.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 @@
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 @@
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 @@
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 @@
// 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 6f1ed85..f647566 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 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