summaryrefslogtreecommitdiff
path: root/java/src
diff options
context:
space:
mode:
author Android Build Coastguard Worker <android-build-coastguard-worker@google.com> 2024-06-13 23:22:59 +0000
committer Android Build Coastguard Worker <android-build-coastguard-worker@google.com> 2024-06-13 23:22:59 +0000
commit973bbc5551e69c874a64d3f531d02fbae508994a (patch)
tree05f34c00cad011a82aef14a105ee0f12abeab42b /java/src
parent633206001031a1a1d805aebad42fde9229e63d0a (diff)
parentb7ca759c198cf9e8f5f606dea9f1602616bba015 (diff)
Snap for 11967491 from b7ca759c198cf9e8f5f606dea9f1602616bba015 to 24Q3-release
Change-Id: Iee8433f6bebd2e84ae31453c4b9c42611e162c85
Diffstat (limited to 'java/src')
-rw-r--r--java/src/com/android/intentresolver/contentpreview/payloadtoggle/data/repository/PreviewSelectionsRepository.kt3
-rw-r--r--java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/cursor/PayloadToggleCursorResolver.kt2
-rw-r--r--java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/CursorPreviewsInteractor.kt32
-rw-r--r--java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/FetchPreviewsInteractor.kt14
-rw-r--r--java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/SelectablePreviewInteractor.kt2
-rw-r--r--java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/SelectionInteractor.kt30
-rw-r--r--java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/model/CursorRow.kt2
-rw-r--r--java/src/com/android/intentresolver/contentpreview/payloadtoggle/shared/model/PreviewModel.kt4
-rw-r--r--java/src/com/android/intentresolver/util/ParallelIteration.kt21
9 files changed, 80 insertions, 30 deletions
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 48c06192..81c56d1e 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,6 +16,7 @@
package com.android.intentresolver.contentpreview.payloadtoggle.data.repository
+import android.net.Uri
import com.android.intentresolver.contentpreview.payloadtoggle.shared.model.PreviewModel
import dagger.hilt.android.scopes.ViewModelScoped
import javax.inject.Inject
@@ -24,5 +25,5 @@ import kotlinx.coroutines.flow.MutableStateFlow
/** Stores set of selected previews. */
@ViewModelScoped
class PreviewSelectionsRepository @Inject constructor() {
- val selections = MutableStateFlow(emptyList<PreviewModel>())
+ val selections = MutableStateFlow(emptyMap<Uri, PreviewModel>())
}
diff --git a/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/cursor/PayloadToggleCursorResolver.kt b/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/cursor/PayloadToggleCursorResolver.kt
index d9612696..148310e6 100644
--- a/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/cursor/PayloadToggleCursorResolver.kt
+++ b/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/cursor/PayloadToggleCursorResolver.kt
@@ -55,7 +55,7 @@ constructor(
)
}
.getOrNull()
- ?.viewBy { readUri()?.let { uri -> CursorRow(uri, readSize()) } }
+ ?.viewBy { readUri()?.let { uri -> CursorRow(uri, readSize(), position) } }
}
private fun Cursor.readUri(): Uri? {
diff --git a/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/CursorPreviewsInteractor.kt b/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/CursorPreviewsInteractor.kt
index fa600c86..a475263c 100644
--- a/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/CursorPreviewsInteractor.kt
+++ b/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/CursorPreviewsInteractor.kt
@@ -56,6 +56,7 @@ class CursorPreviewsInteractor
@Inject
constructor(
private val interactor: SetCursorPreviewsInteractor,
+ private val selectionInteractor: SelectionInteractor,
@FocusedItemIndex private val focusedItemIdx: Int,
private val uriMetadataReader: UriMetadataReader,
@PageSize private val pageSize: Int,
@@ -287,19 +288,26 @@ constructor(
private fun createPreviewModel(
row: CursorRow,
unclaimedRecords: MutableUnclaimedMap,
- ): PreviewModel =
- unclaimedRecords.remove(row.uri)?.second
- ?: uriMetadataReader.getMetadata(row.uri).let { metadata ->
- val size =
- row.previewSize
- ?: metadata.previewUri?.let { uriMetadataReader.readPreviewSize(it) }
- PreviewModel(
- uri = row.uri,
- previewUri = metadata.previewUri,
- mimeType = metadata.mimeType,
- aspectRatio = size.aspectRatioOrDefault(1f),
- )
+ ): PreviewModel = uriMetadataReader.getMetadata(row.uri).let { metadata ->
+ val size =
+ row.previewSize
+ ?: metadata.previewUri?.let { uriMetadataReader.readPreviewSize(it) }
+ PreviewModel(
+ uri = row.uri,
+ previewUri = metadata.previewUri,
+ mimeType = metadata.mimeType,
+ aspectRatio = size.aspectRatioOrDefault(1f),
+ order = row.position,
+ )
+ }.also { updated ->
+ if (unclaimedRecords.remove(row.uri) != null) {
+ // unclaimedRecords contains initially shared (and thus selected) items with unknown
+ // cursor position. Update selection records when any of those items is encountered
+ // in the cursor to maintain proper selection order should other items also be
+ // selected.
+ selectionInteractor.updateSelection(updated)
}
+ }
private fun <M : MutablePreviewMap> M.putAllUnclaimedRight(unclaimed: UnclaimedMap): M =
putAllUnclaimedWhere(unclaimed) { it >= focusedItemIdx }
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 c9c9a9b3..50086a23 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
@@ -25,7 +25,7 @@ import com.android.intentresolver.contentpreview.payloadtoggle.domain.model.Curs
import com.android.intentresolver.contentpreview.payloadtoggle.shared.model.PreviewModel
import com.android.intentresolver.inject.ContentUris
import com.android.intentresolver.inject.FocusedItemIndex
-import com.android.intentresolver.util.mapParallel
+import com.android.intentresolver.util.mapParallelIndexed
import javax.inject.Inject
import kotlinx.coroutines.async
import kotlinx.coroutines.coroutineScope
@@ -45,7 +45,7 @@ constructor(
suspend fun activate() = coroutineScope {
val cursor = async { cursorResolver.getCursor() }
val initialPreviewMap = getInitialPreviews()
- selectionRepository.selections.value = initialPreviewMap
+ selectionRepository.selections.value = initialPreviewMap.associateBy { it.uri }
setCursorPreviews.setPreviews(
previews = initialPreviewMap,
startIndex = focusedItemIdx,
@@ -61,7 +61,7 @@ constructor(
selectedItems
// Restrict parallelism so as to not overload the metadata reader; anecdotally, too
// many parallel queries causes failures.
- .mapParallel(parallelism = 4) { uri ->
+ .mapParallelIndexed(parallelism = 4) { index, uri ->
val metadata = uriMetadataReader.getMetadata(uri)
PreviewModel(
uri = uri,
@@ -70,8 +70,12 @@ constructor(
aspectRatio =
metadata.previewUri?.let {
uriMetadataReader.readPreviewSize(it).aspectRatioOrDefault(1f)
- }
- ?: 1f,
+ } ?: 1f,
+ order = when {
+ index < focusedItemIdx -> Int.MIN_VALUE + index
+ index == focusedItemIdx -> 0
+ else -> Int.MAX_VALUE - selectedItems.size + index + 1
+ }
)
}
}
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 55a995f5..d52a71a1 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
@@ -29,7 +29,7 @@ class SelectablePreviewInteractor(
val uri: Uri = key.uri
/** Whether or not this preview is selected by the user. */
- val isSelected: Flow<Boolean> = selectionInteractor.selections.map { key in it }
+ val isSelected: Flow<Boolean> = selectionInteractor.selections.map { key.uri in it }
/** Sets whether this preview is selected by the user. */
fun setSelected(isSelected: Boolean) {
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 13af92cb..97d9fa66 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
@@ -16,6 +16,7 @@
package com.android.intentresolver.contentpreview.payloadtoggle.domain.interactor
+import android.net.Uri
import com.android.intentresolver.contentpreview.MimeTypeClassifier
import com.android.intentresolver.contentpreview.payloadtoggle.data.repository.PreviewSelectionsRepository
import com.android.intentresolver.contentpreview.payloadtoggle.domain.intent.TargetIntentModifier
@@ -23,8 +24,9 @@ import com.android.intentresolver.contentpreview.payloadtoggle.shared.ContentTyp
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.distinctUntilChanged
import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.update
import kotlinx.coroutines.flow.updateAndGet
class SelectionInteractor
@@ -36,31 +38,41 @@ constructor(
private val mimeTypeClassifier: MimeTypeClassifier,
) {
/** List of selected previews. */
- val selections: StateFlow<List<PreviewModel>>
- get() = selectionsRepo.selections
+ val selections: Flow<Set<Uri>> =
+ selectionsRepo.selections.map { it.keys }.distinctUntilChanged()
/** Amount of selected previews. */
val amountSelected: Flow<Int> = selectionsRepo.selections.map { it.size }
- val aggregateContentType: Flow<ContentType> = selections.map { aggregateContentType(it) }
+ val aggregateContentType: Flow<ContentType> =
+ selectionsRepo.selections.map { aggregateContentType(it.values) }
+
+ fun updateSelection(model: PreviewModel) {
+ selectionsRepo.selections.update {
+ if (it.containsKey(model.uri)) it + (model.uri to model) else it
+ }
+ }
fun select(model: PreviewModel) {
- updateChooserRequest(selectionsRepo.selections.updateAndGet { it + model })
+ updateChooserRequest(
+ selectionsRepo.selections.updateAndGet { it + (model.uri to model) }.values
+ )
}
fun unselect(model: PreviewModel) {
if (selectionsRepo.selections.value.size > 1) {
- updateChooserRequest(selectionsRepo.selections.updateAndGet { it - model })
+ updateChooserRequest(selectionsRepo.selections.updateAndGet { it - model.uri }.values)
}
}
- private fun updateChooserRequest(selections: List<PreviewModel>) {
- val intent = targetIntentModifier.intentFromSelection(selections)
+ private fun updateChooserRequest(selections: Collection<PreviewModel>) {
+ val sorted = selections.sortedBy { it.order }
+ val intent = targetIntentModifier.intentFromSelection(sorted)
updateTargetIntentInteractor.updateTargetIntent(intent)
}
private fun aggregateContentType(
- items: List<PreviewModel>,
+ items: Collection<PreviewModel>,
): ContentType {
if (items.isEmpty()) {
return ContentType.Other
diff --git a/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/model/CursorRow.kt b/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/model/CursorRow.kt
index f1d856ac..aae29102 100644
--- a/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/model/CursorRow.kt
+++ b/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/model/CursorRow.kt
@@ -20,4 +20,4 @@ import android.net.Uri
import android.util.Size
/** Represents additional content cursor row */
-data class CursorRow(val uri: Uri, val previewSize: Size?)
+data class CursorRow(val uri: Uri, val previewSize: Size?, val position: Int)
diff --git a/java/src/com/android/intentresolver/contentpreview/payloadtoggle/shared/model/PreviewModel.kt b/java/src/com/android/intentresolver/contentpreview/payloadtoggle/shared/model/PreviewModel.kt
index 85c70004..8a479156 100644
--- a/java/src/com/android/intentresolver/contentpreview/payloadtoggle/shared/model/PreviewModel.kt
+++ b/java/src/com/android/intentresolver/contentpreview/payloadtoggle/shared/model/PreviewModel.kt
@@ -27,4 +27,8 @@ data class PreviewModel(
/** Mimetype for the data [uri] points to. */
val mimeType: String?,
val aspectRatio: Float = 1f,
+ /**
+ * Relative item position in the list that is used to determine items order in the target intent
+ */
+ val order: Int,
)
diff --git a/java/src/com/android/intentresolver/util/ParallelIteration.kt b/java/src/com/android/intentresolver/util/ParallelIteration.kt
index 70c46c47..745bcdbf 100644
--- a/java/src/com/android/intentresolver/util/ParallelIteration.kt
+++ b/java/src/com/android/intentresolver/util/ParallelIteration.kt
@@ -48,3 +48,24 @@ private suspend fun <A, B> Iterable<A>.mapParallel(block: suspend (A) -> B): Lis
}
.awaitAll()
}
+
+suspend fun <A, B> Iterable<A>.mapParallelIndexed(
+ parallelism: Int? = null,
+ block: suspend (Int, A) -> B,
+): List<B> =
+ parallelism?.let { permits ->
+ withSemaphore(permits = permits) {
+ mapParallelIndexed { idx, item -> withPermit { block(idx, item) } }
+ }
+ } ?: mapParallelIndexed(block)
+
+private suspend fun <A, B> Iterable<A>.mapParallelIndexed(block: suspend (Int, A) -> B): List<B> =
+ coroutineScope {
+ mapIndexed { index, item ->
+ async {
+ yield()
+ block(index, item)
+ }
+ }
+ .awaitAll()
+ }