summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Treehugger Robot <android-test-infra-autosubmit@system.gserviceaccount.com> 2024-05-24 15:23:55 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2024-05-24 15:23:55 +0000
commitad07ab8f46bbaba16b00c76960f6421312b38689 (patch)
tree26934d39f202f5de8380afb5834b904bd7426674
parent160289301fe214fa092f43cb0581a8688dca2487 (diff)
parent7bd1caf07c36f117bccd8bd462e3b9491b91acd5 (diff)
Merge "Honor content types in shareousel" into main
-rw-r--r--java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/SelectionInteractor.kt30
-rw-r--r--java/src/com/android/intentresolver/contentpreview/payloadtoggle/shared/ContentType.kt24
-rw-r--r--java/src/com/android/intentresolver/contentpreview/payloadtoggle/ui/composable/ShareouselCardComposable.kt2
-rw-r--r--java/src/com/android/intentresolver/contentpreview/payloadtoggle/ui/composable/ShareouselComposable.kt8
-rw-r--r--java/src/com/android/intentresolver/contentpreview/payloadtoggle/ui/viewmodel/ShareouselPreviewViewModel.kt10
-rw-r--r--java/src/com/android/intentresolver/contentpreview/payloadtoggle/ui/viewmodel/ShareouselViewModel.kt18
-rw-r--r--tests/shared/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/PayloadToggleInteractorKosmos.kt2
-rw-r--r--tests/unit/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/SelectionInteractorTest.kt7
-rw-r--r--tests/unit/src/com/android/intentresolver/contentpreview/payloadtoggle/ui/viewmodel/ShareouselViewModelTest.kt56
9 files changed, 128 insertions, 29 deletions
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 802e58a2..e99aa50c 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,8 +16,10 @@
package com.android.intentresolver.contentpreview.payloadtoggle.domain.interactor
+import com.android.intentresolver.contentpreview.MimeTypeClassifier
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.ContentType
import com.android.intentresolver.contentpreview.payloadtoggle.shared.model.PreviewModel
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
@@ -31,6 +33,7 @@ constructor(
private val selectionsRepo: PreviewSelectionsRepository,
private val targetIntentModifier: TargetIntentModifier<PreviewModel>,
private val updateTargetIntentInteractor: UpdateTargetIntentInteractor,
+ private val mimeTypeClassifier: MimeTypeClassifier,
) {
/** Set of selected previews. */
val selections: StateFlow<Set<PreviewModel>>
@@ -39,6 +42,8 @@ constructor(
/** Amount of selected previews. */
val amountSelected: Flow<Int> = selectionsRepo.selections.map { it.size }
+ val aggregateContentType: Flow<ContentType> = selections.map { aggregateContentType(it) }
+
fun select(model: PreviewModel) {
updateChooserRequest(selectionsRepo.selections.updateAndGet { it + model })
}
@@ -53,4 +58,29 @@ constructor(
val intent = targetIntentModifier.intentFromSelection(selections)
updateTargetIntentInteractor.updateTargetIntent(intent)
}
+
+ private fun aggregateContentType(
+ items: Set<PreviewModel>,
+ ): ContentType {
+ if (items.isEmpty()) {
+ return ContentType.Other
+ }
+
+ var allImages = true
+ var allVideos = true
+ for (item in items) {
+ allImages = allImages && mimeTypeClassifier.isImageType(item.mimeType)
+ allVideos = allVideos && mimeTypeClassifier.isVideoType(item.mimeType)
+
+ if (!allImages && !allVideos) {
+ break
+ }
+ }
+
+ return when {
+ allImages -> ContentType.Image
+ allVideos -> ContentType.Video
+ else -> ContentType.Other
+ }
+ }
}
diff --git a/java/src/com/android/intentresolver/contentpreview/payloadtoggle/shared/ContentType.kt b/java/src/com/android/intentresolver/contentpreview/payloadtoggle/shared/ContentType.kt
new file mode 100644
index 00000000..3ef6d98f
--- /dev/null
+++ b/java/src/com/android/intentresolver/contentpreview/payloadtoggle/shared/ContentType.kt
@@ -0,0 +1,24 @@
+/*
+ * 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.shared
+
+/** Type of the content being previewed. */
+enum class ContentType {
+ Image,
+ Video,
+ Other
+}
diff --git a/java/src/com/android/intentresolver/contentpreview/payloadtoggle/ui/composable/ShareouselCardComposable.kt b/java/src/com/android/intentresolver/contentpreview/payloadtoggle/ui/composable/ShareouselCardComposable.kt
index c2330ad8..a0be1a9b 100644
--- a/java/src/com/android/intentresolver/contentpreview/payloadtoggle/ui/composable/ShareouselCardComposable.kt
+++ b/java/src/com/android/intentresolver/contentpreview/payloadtoggle/ui/composable/ShareouselCardComposable.kt
@@ -33,7 +33,7 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import com.android.intentresolver.R
-import com.android.intentresolver.contentpreview.payloadtoggle.ui.viewmodel.ContentType
+import com.android.intentresolver.contentpreview.payloadtoggle.shared.ContentType
@Composable
fun ShareouselCard(
diff --git a/java/src/com/android/intentresolver/contentpreview/payloadtoggle/ui/composable/ShareouselComposable.kt b/java/src/com/android/intentresolver/contentpreview/payloadtoggle/ui/composable/ShareouselComposable.kt
index 36c94b59..c25b0154 100644
--- a/java/src/com/android/intentresolver/contentpreview/payloadtoggle/ui/composable/ShareouselComposable.kt
+++ b/java/src/com/android/intentresolver/contentpreview/payloadtoggle/ui/composable/ShareouselComposable.kt
@@ -56,8 +56,8 @@ import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.intentresolver.R
+import com.android.intentresolver.contentpreview.payloadtoggle.shared.ContentType
import com.android.intentresolver.contentpreview.payloadtoggle.shared.model.PreviewsModel
-import com.android.intentresolver.contentpreview.payloadtoggle.ui.viewmodel.ContentType
import com.android.intentresolver.contentpreview.payloadtoggle.ui.viewmodel.ShareouselPreviewViewModel
import com.android.intentresolver.contentpreview.payloadtoggle.ui.viewmodel.ShareouselViewModel
import kotlinx.coroutines.launch
@@ -114,12 +114,10 @@ private fun PreviewCarousel(
private fun ShareouselCard(viewModel: ShareouselPreviewViewModel) {
val bitmap by viewModel.bitmap.collectAsStateWithLifecycle(initialValue = null)
val selected by viewModel.isSelected.collectAsStateWithLifecycle(initialValue = false)
- val contentType by
- viewModel.contentType.collectAsStateWithLifecycle(initialValue = ContentType.Image)
val borderColor = MaterialTheme.colorScheme.primary
val scope = rememberCoroutineScope()
val contentDescription =
- when (contentType) {
+ when (viewModel.contentType) {
ContentType.Image -> stringResource(R.string.selectable_image)
ContentType.Video -> stringResource(R.string.selectable_video)
else -> stringResource(R.string.selectable_item)
@@ -141,7 +139,7 @@ private fun ShareouselCard(viewModel: ShareouselPreviewViewModel) {
Box(modifier = Modifier.fillMaxHeight().aspectRatio(aspectRatio))
}
},
- contentType = contentType,
+ contentType = viewModel.contentType,
selected = selected,
modifier =
Modifier.thenIf(selected) {
diff --git a/java/src/com/android/intentresolver/contentpreview/payloadtoggle/ui/viewmodel/ShareouselPreviewViewModel.kt b/java/src/com/android/intentresolver/contentpreview/payloadtoggle/ui/viewmodel/ShareouselPreviewViewModel.kt
index 9827fcd4..540229c9 100644
--- a/java/src/com/android/intentresolver/contentpreview/payloadtoggle/ui/viewmodel/ShareouselPreviewViewModel.kt
+++ b/java/src/com/android/intentresolver/contentpreview/payloadtoggle/ui/viewmodel/ShareouselPreviewViewModel.kt
@@ -17,6 +17,7 @@
package com.android.intentresolver.contentpreview.payloadtoggle.ui.viewmodel
import android.graphics.Bitmap
+import com.android.intentresolver.contentpreview.payloadtoggle.shared.ContentType
import kotlinx.coroutines.flow.Flow
/** An individual preview within Shareousel. */
@@ -24,17 +25,10 @@ data class ShareouselPreviewViewModel(
/** Image to be shared. */
val bitmap: Flow<Bitmap?>,
/** Type of data to be shared. */
- val contentType: Flow<ContentType>,
+ val contentType: ContentType,
/** Whether this preview has been selected by the user. */
val isSelected: Flow<Boolean>,
/** Sets whether this preview has been selected by the user. */
val setSelected: suspend (Boolean) -> Unit,
val aspectRatio: Float,
)
-
-/** Type of the content being previewed. */
-enum class ContentType {
- Image,
- Video,
- Other
-}
diff --git a/java/src/com/android/intentresolver/contentpreview/payloadtoggle/ui/viewmodel/ShareouselViewModel.kt b/java/src/com/android/intentresolver/contentpreview/payloadtoggle/ui/viewmodel/ShareouselViewModel.kt
index 1b9c231b..4eda3fa9 100644
--- a/java/src/com/android/intentresolver/contentpreview/payloadtoggle/ui/viewmodel/ShareouselViewModel.kt
+++ b/java/src/com/android/intentresolver/contentpreview/payloadtoggle/ui/viewmodel/ShareouselViewModel.kt
@@ -18,11 +18,13 @@ package com.android.intentresolver.contentpreview.payloadtoggle.ui.viewmodel
import com.android.intentresolver.contentpreview.CachingImagePreviewImageLoader
import com.android.intentresolver.contentpreview.HeadlineGenerator
import com.android.intentresolver.contentpreview.ImageLoader
+import com.android.intentresolver.contentpreview.MimeTypeClassifier
import com.android.intentresolver.contentpreview.payloadtoggle.domain.cursor.PayloadToggle
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.shared.ContentType
import com.android.intentresolver.contentpreview.payloadtoggle.shared.model.PreviewModel
import com.android.intentresolver.contentpreview.payloadtoggle.shared.model.PreviewsModel
import com.android.intentresolver.inject.ViewModelOwned
@@ -35,9 +37,9 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.flow
-import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.flow.zip
/** A dynamic carousel of selectable previews within share sheet. */
data class ShareouselViewModel(
@@ -71,6 +73,7 @@ interface ShareouselViewModelModule {
headlineGenerator: HeadlineGenerator,
selectionInteractor: SelectionInteractor,
chooserRequestInteractor: ChooserRequestInteractor,
+ mimeTypeClassifier: MimeTypeClassifier,
// TODO: remove if possible
@ViewModelOwned scope: CoroutineScope,
): ShareouselViewModel {
@@ -82,8 +85,9 @@ interface ShareouselViewModelModule {
)
return ShareouselViewModel(
headline =
- selectionInteractor.amountSelected.map { numItems ->
- val contentType = ContentType.Image // TODO: convert from metadata
+ selectionInteractor.aggregateContentType.zip(
+ selectionInteractor.amountSelected
+ ) { contentType, numItems ->
when (contentType) {
ContentType.Other -> headlineGenerator.getFilesHeadline(numItems)
ContentType.Image -> headlineGenerator.getImagesHeadline(numItems)
@@ -111,9 +115,15 @@ interface ShareouselViewModelModule {
preview = { key ->
keySet.value?.maybeLoad(key)
val previewInteractor = interactor.preview(key)
+ val contentType =
+ when {
+ mimeTypeClassifier.isImageType(key.mimeType) -> ContentType.Image
+ mimeTypeClassifier.isVideoType(key.mimeType) -> ContentType.Video
+ else -> ContentType.Other
+ }
ShareouselPreviewViewModel(
bitmap = flow { emit(key.previewUri?.let { imageLoader(it) }) },
- contentType = flowOf(ContentType.Image), // TODO: convert from metadata
+ contentType = contentType,
isSelected = previewInteractor.isSelected,
setSelected = previewInteractor::setSelected,
aspectRatio = key.aspectRatio,
diff --git a/tests/shared/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/PayloadToggleInteractorKosmos.kt b/tests/shared/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/PayloadToggleInteractorKosmos.kt
index 659c178c..8f7c59de 100644
--- a/tests/shared/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/PayloadToggleInteractorKosmos.kt
+++ b/tests/shared/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/PayloadToggleInteractorKosmos.kt
@@ -20,6 +20,7 @@ import com.android.intentresolver.backgroundDispatcher
import com.android.intentresolver.contentResolver
import com.android.intentresolver.contentpreview.HeadlineGenerator
import com.android.intentresolver.contentpreview.ImageLoader
+import com.android.intentresolver.contentpreview.mimetypeClassifier
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
@@ -97,6 +98,7 @@ val Kosmos.selectionInteractor
selectionsRepo = previewSelectionsRepository,
targetIntentModifier = targetIntentModifier,
updateTargetIntentInteractor = updateTargetIntentInteractor,
+ mimeTypeClassifier = mimetypeClassifier,
)
val Kosmos.setCursorPreviewsInteractor
diff --git a/tests/unit/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/SelectionInteractorTest.kt b/tests/unit/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/SelectionInteractorTest.kt
index a64807b7..708e6cc6 100644
--- a/tests/unit/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/SelectionInteractorTest.kt
+++ b/tests/unit/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/SelectionInteractorTest.kt
@@ -18,6 +18,7 @@ package com.android.intentresolver.contentpreview.payloadtoggle.domain.interacto
import android.content.Intent
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.shared.model.PreviewModel
import com.android.intentresolver.util.runKosmosTest
@@ -35,7 +36,8 @@ class SelectionInteractorTest {
SelectionInteractor(
previewSelectionsRepository,
{ Intent() },
- updateTargetIntentInteractor
+ updateTargetIntentInteractor,
+ mimetypeClassifier,
)
assertThat(underTest.selections.value).isEqualTo(setOf(initialPreview))
@@ -57,7 +59,8 @@ class SelectionInteractorTest {
SelectionInteractor(
previewSelectionsRepository,
{ Intent() },
- updateTargetIntentInteractor
+ updateTargetIntentInteractor,
+ mimetypeClassifier
)
underTest.unselect(first)
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 bd3d88f8..fb3e9a3f 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
@@ -25,6 +25,7 @@ import android.graphics.drawable.Icon
import android.net.Uri
import com.android.intentresolver.FakeImageLoader
import com.android.intentresolver.contentpreview.HeadlineGenerator
+import com.android.intentresolver.contentpreview.mimetypeClassifier
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
@@ -39,6 +40,7 @@ import com.android.intentresolver.contentpreview.payloadtoggle.domain.interactor
import com.android.intentresolver.contentpreview.payloadtoggle.domain.interactor.payloadToggleImageLoader
import com.android.intentresolver.contentpreview.payloadtoggle.domain.interactor.selectablePreviewsInteractor
import com.android.intentresolver.contentpreview.payloadtoggle.domain.interactor.selectionInteractor
+import com.android.intentresolver.contentpreview.payloadtoggle.shared.ContentType
import com.android.intentresolver.contentpreview.payloadtoggle.shared.model.PreviewModel
import com.android.intentresolver.contentpreview.payloadtoggle.shared.model.PreviewsModel
import com.android.intentresolver.data.model.ChooserRequest
@@ -68,23 +70,24 @@ class ShareouselViewModelTest {
actionsInteractor = customActionsInteractor,
headlineGenerator = headlineGenerator,
chooserRequestInteractor = chooserRequestInteractor,
+ mimeTypeClassifier = mimetypeClassifier,
selectionInteractor = selectionInteractor,
scope = viewModelScope,
)
}
@Test
- fun headline() = runTest {
- assertThat(shareouselViewModel.headline.first()).isEqualTo("IMAGES: 1")
+ fun headline_images() = runTest {
+ assertThat(shareouselViewModel.headline.first()).isEqualTo("FILES: 1")
previewSelectionsRepository.selections.value =
setOf(
PreviewModel(
uri = Uri.fromParts("scheme", "ssp", "fragment"),
- mimeType = null,
+ mimeType = "image/png",
),
PreviewModel(
uri = Uri.fromParts("scheme1", "ssp1", "fragment1"),
- mimeType = null,
+ mimeType = "image/jpeg",
)
)
runCurrent()
@@ -92,6 +95,40 @@ class ShareouselViewModelTest {
}
@Test
+ fun headline_videos() = runTest {
+ previewSelectionsRepository.selections.value =
+ setOf(
+ PreviewModel(
+ uri = Uri.fromParts("scheme", "ssp", "fragment"),
+ mimeType = "video/mpeg",
+ ),
+ PreviewModel(
+ uri = Uri.fromParts("scheme1", "ssp1", "fragment1"),
+ mimeType = "video/mpeg",
+ )
+ )
+ runCurrent()
+ assertThat(shareouselViewModel.headline.first()).isEqualTo("VIDEOS: 2")
+ }
+
+ @Test
+ fun headline_mixed() = runTest {
+ previewSelectionsRepository.selections.value =
+ setOf(
+ PreviewModel(
+ uri = Uri.fromParts("scheme", "ssp", "fragment"),
+ mimeType = "image/jpeg",
+ ),
+ PreviewModel(
+ uri = Uri.fromParts("scheme1", "ssp1", "fragment1"),
+ mimeType = "video/mpeg",
+ )
+ )
+ runCurrent()
+ assertThat(shareouselViewModel.headline.first()).isEqualTo("FILES: 2")
+ }
+
+ @Test
fun metadataText() = runTest {
val request =
ChooserRequest(
@@ -115,11 +152,11 @@ class ShareouselViewModelTest {
setOf(
PreviewModel(
uri = Uri.fromParts("scheme", "ssp", "fragment"),
- mimeType = null,
+ mimeType = "image/png",
),
PreviewModel(
uri = Uri.fromParts("scheme1", "ssp1", "fragment1"),
- mimeType = null,
+ mimeType = "video/mpeg",
)
),
startIdx = 1,
@@ -143,12 +180,13 @@ class ShareouselViewModelTest {
shareouselViewModel.preview(
PreviewModel(
uri = Uri.fromParts("scheme1", "ssp1", "fragment1"),
- mimeType = null
+ mimeType = "video/mpeg"
)
)
assertWithMessage("preview bitmap is null").that(previewVm.bitmap.first()).isNotNull()
assertThat(previewVm.isSelected.first()).isFalse()
+ assertThat(previewVm.contentType).isEqualTo(ContentType.Video)
previewVm.setSelected(true)
@@ -234,9 +272,9 @@ class ShareouselViewModelTest {
override fun getFilesWithTextHeadline(text: CharSequence, count: Int): String =
error("not supported")
- override fun getVideosHeadline(count: Int): String = error("not supported")
+ override fun getVideosHeadline(count: Int): String = "VIDEOS: $count"
- override fun getFilesHeadline(count: Int): String = error("not supported")
+ override fun getFilesHeadline(count: Int): String = "FILES: $count"
}
// instantiate the view model, and then runCurrent() so that it is fully hydrated before
// starting the test