summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--java/src/com/android/intentresolver/contentpreview/FileInfo.kt3
-rw-r--r--java/src/com/android/intentresolver/contentpreview/UriMetadataReader.kt14
-rw-r--r--java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/CursorPreviewsInteractor.kt4
-rw-r--r--java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/FetchPreviewsInteractor.kt5
-rw-r--r--java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/SizeExtensions.kt26
-rw-r--r--java/src/com/android/intentresolver/contentpreview/payloadtoggle/shared/model/PreviewModel.kt1
-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.kt1
-rw-r--r--java/src/com/android/intentresolver/contentpreview/payloadtoggle/ui/viewmodel/ShareouselViewModel.kt1
-rw-r--r--tests/unit/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/CursorPreviewsInteractorTest.kt107
-rw-r--r--tests/unit/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/FetchPreviewsInteractorTest.kt60
11 files changed, 163 insertions, 67 deletions
diff --git a/java/src/com/android/intentresolver/contentpreview/FileInfo.kt b/java/src/com/android/intentresolver/contentpreview/FileInfo.kt
index fe35365b..16a948df 100644
--- a/java/src/com/android/intentresolver/contentpreview/FileInfo.kt
+++ b/java/src/com/android/intentresolver/contentpreview/FileInfo.kt
@@ -22,8 +22,11 @@ class FileInfo private constructor(val uri: Uri, val previewUri: Uri?, val mimeT
@VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
class Builder(val uri: Uri) {
var previewUri: Uri? = null
+ @Synchronized get
private set
+
var mimeType: String? = null
+ @Synchronized get
private set
@Synchronized fun withPreviewUri(uri: Uri?): Builder = apply { previewUri = uri }
diff --git a/java/src/com/android/intentresolver/contentpreview/UriMetadataReader.kt b/java/src/com/android/intentresolver/contentpreview/UriMetadataReader.kt
index b5361889..4e403c22 100644
--- a/java/src/com/android/intentresolver/contentpreview/UriMetadataReader.kt
+++ b/java/src/com/android/intentresolver/contentpreview/UriMetadataReader.kt
@@ -20,6 +20,8 @@ import android.content.ContentInterface
import android.media.MediaMetadata
import android.net.Uri
import android.provider.DocumentsContract
+import android.provider.MediaStore.MediaColumns
+import android.util.Size
import dagger.Binds
import dagger.Module
import dagger.Provides
@@ -29,6 +31,7 @@ import javax.inject.Inject
fun interface UriMetadataReader {
fun getMetadata(uri: Uri): FileInfo
+ fun readPreviewSize(uri: Uri): Size? = null
}
class UriMetadataReaderImpl
@@ -56,6 +59,8 @@ constructor(
return builder.build()
}
+ override fun readPreviewSize(uri: Uri): Size? = contentResolver.readPreviewSize(uri)
+
private fun ContentInterface.supportsImageType(uri: Uri): Boolean =
getStreamTypesSafe(uri).firstOrNull { typeClassifier.isImageType(it) } != null
@@ -73,6 +78,15 @@ constructor(
null
}
}
+
+ private fun ContentInterface.readPreviewSize(uri: Uri): Size? =
+ querySafe(uri, arrayOf(MediaColumns.WIDTH, MediaColumns.HEIGHT))?.use { cursor ->
+ if (cursor.moveToFirst()) {
+ cursor.readSize()
+ } else {
+ null
+ }
+ }
}
@Module
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 c7d29a72..97b087e1 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
@@ -242,10 +242,14 @@ constructor(
): 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),
)
}
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 c87504e1..80cd03d9 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
@@ -65,6 +65,11 @@ constructor(
uri = uri,
previewUri = metadata.previewUri,
mimeType = metadata.mimeType,
+ aspectRatio =
+ metadata.previewUri?.let {
+ uriMetadataReader.readPreviewSize(it).aspectRatioOrDefault(1f)
+ }
+ ?: 1f,
)
}
.toSet()
diff --git a/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/SizeExtensions.kt b/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/SizeExtensions.kt
new file mode 100644
index 00000000..4cf10414
--- /dev/null
+++ b/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/SizeExtensions.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.intentresolver.contentpreview.payloadtoggle.domain.interactor
+
+import android.util.Size
+
+internal fun Size?.aspectRatioOrDefault(default: Float): Float =
+ when {
+ this == null -> default
+ width >= 0 && height > 0 -> width.toFloat() / height.toFloat()
+ else -> default
+ }
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 6b805391..85c70004 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
@@ -26,4 +26,5 @@ data class PreviewModel(
val previewUri: Uri? = uri,
/** Mimetype for the data [uri] points to. */
val mimeType: String?,
+ val aspectRatio: Float = 1f,
)
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 0a431c2a..85ad6ab3 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
@@ -115,11 +115,9 @@ private fun ShareouselCard(viewModel: ShareouselPreviewViewModel) {
val scope = rememberCoroutineScope()
ShareouselCard(
image = {
+ // TODO: max ratio is actually equal to the viewport ratio
+ val aspectRatio = viewModel.aspectRatio.coerceIn(MIN_ASPECT_RATIO, MAX_ASPECT_RATIO)
bitmap?.let { bitmap ->
- val aspectRatio =
- (bitmap.width.toFloat() / bitmap.height.toFloat())
- // TODO: max ratio is actually equal to the viewport ratio
- .coerceIn(MIN_ASPECT_RATIO, MAX_ASPECT_RATIO)
Image(
bitmap = bitmap.asImageBitmap(),
contentDescription = null,
@@ -129,7 +127,7 @@ private fun ShareouselCard(viewModel: ShareouselPreviewViewModel) {
}
?: run {
// TODO: look at ScrollableImagePreviewView.setLoading()
- Box(modifier = Modifier.fillMaxHeight().aspectRatio(2f / 5f))
+ Box(modifier = Modifier.fillMaxHeight().aspectRatio(aspectRatio))
}
},
contentType = contentType,
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 a245b3e3..9827fcd4 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
@@ -29,6 +29,7 @@ data class ShareouselPreviewViewModel(
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. */
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 cf118934..8b2dd818 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
@@ -117,6 +117,7 @@ object ShareouselViewModelModule {
contentType = flowOf(ContentType.Image), // TODO: convert from metadata
isSelected = previewInteractor.isSelected,
setSelected = previewInteractor::setSelected,
+ aspectRatio = key.aspectRatio,
)
},
)
diff --git a/tests/unit/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/CursorPreviewsInteractorTest.kt b/tests/unit/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/CursorPreviewsInteractorTest.kt
index 9b786b74..ff699373 100644
--- a/tests/unit/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/CursorPreviewsInteractorTest.kt
+++ b/tests/unit/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/CursorPreviewsInteractorTest.kt
@@ -20,12 +20,16 @@ package com.android.intentresolver.contentpreview.payloadtoggle.domain.interacto
import android.database.MatrixCursor
import android.net.Uri
+import android.provider.MediaStore.MediaColumns.HEIGHT
+import android.provider.MediaStore.MediaColumns.WIDTH
+import android.util.Size
import androidx.core.os.bundleOf
import com.android.intentresolver.contentpreview.FileInfo
import com.android.intentresolver.contentpreview.UriMetadataReader
import com.android.intentresolver.contentpreview.payloadtoggle.data.repository.cursorPreviewsRepository
import com.android.intentresolver.contentpreview.payloadtoggle.domain.model.CursorRow
import com.android.intentresolver.contentpreview.payloadtoggle.shared.model.PreviewModel
+import com.android.intentresolver.contentpreview.readSize
import com.android.intentresolver.contentpreview.uriMetadataReader
import com.android.intentresolver.util.KosmosTestScope
import com.android.intentresolver.util.cursor.CursorView
@@ -46,21 +50,32 @@ class CursorPreviewsInteractorTest {
cursorStartPosition: Int = cursor.count() / 2,
pageSize: Int = 16,
maxLoadedPages: Int = 3,
+ cursorSizes: Map<Int, Size> = emptyMap(),
+ metadatSizes: Map<Int, Size> = emptyMap(),
block: KosmosTestScope.(TestDeps) -> Unit,
) {
+ val metadataUriToSize = metadatSizes.mapKeys { uri(it.key) }
with(Kosmos()) {
this.focusedItemIndex = focusedItemIndex
this.pageSize = pageSize
this.maxLoadedPages = maxLoadedPages
- uriMetadataReader = UriMetadataReader {
- FileInfo.Builder(it).withPreviewUri(it).withMimeType("image/bitmap").build()
- }
+ uriMetadataReader =
+ object : UriMetadataReader {
+ override fun getMetadata(uri: Uri): FileInfo =
+ FileInfo.Builder(uri)
+ .withPreviewUri(uri)
+ .withMimeType("image/bitmap")
+ .build()
+
+ override fun readPreviewSize(uri: Uri): Size? = metadataUriToSize[uri]
+ }
runTest {
block(
TestDeps(
initialSelection,
cursor,
cursorStartPosition,
+ cursorSizes,
)
)
}
@@ -71,54 +86,66 @@ class CursorPreviewsInteractorTest {
initialSelectionRange: Iterable<Int>,
private val cursorRange: Iterable<Int>,
private val cursorStartPosition: Int,
+ private val cursorSizes: Map<Int, Size>,
) {
val cursor: CursorView<CursorRow?> =
- MatrixCursor(arrayOf("uri"))
+ MatrixCursor(arrayOf("uri", WIDTH, HEIGHT))
.apply {
extras = bundleOf("position" to cursorStartPosition)
for (i in cursorRange) {
- newRow().add("uri", uri(i).toString())
+ val size = cursorSizes[i]
+ addRow(
+ arrayOf(
+ uri(i).toString(),
+ size?.width?.toString(),
+ size?.height?.toString(),
+ )
+ )
}
}
- .viewBy { getString(0)?.let { uriStr -> CursorRow(Uri.parse(uriStr), null) } }
+ .viewBy { getString(0)?.let { uriStr -> CursorRow(Uri.parse(uriStr), readSize()) } }
val initialPreviews: List<PreviewModel> =
initialSelectionRange.map { i -> PreviewModel(uri = uri(i), mimeType = "image/bitmap") }
-
- private fun uri(index: Int) = Uri.fromParts("scheme$index", "ssp$index", "fragment$index")
}
@Test
- fun initialCursorLoad() = runTestWithDeps { deps ->
- backgroundScope.launch {
- cursorPreviewsInteractor.launch(deps.cursor, deps.initialPreviews)
- }
- runCurrent()
+ fun initialCursorLoad() =
+ runTestWithDeps(
+ cursorSizes = mapOf(0 to (200 x 100)),
+ metadatSizes = mapOf(0 to (300 x 100), 3 to (400 x 100))
+ ) { deps ->
+ backgroundScope.launch {
+ cursorPreviewsInteractor.launch(deps.cursor, deps.initialPreviews)
+ }
+ runCurrent()
- assertThat(cursorPreviewsRepository.previewsModel.value).isNotNull()
- assertThat(cursorPreviewsRepository.previewsModel.value!!.startIdx).isEqualTo(0)
- assertThat(cursorPreviewsRepository.previewsModel.value!!.loadMoreLeft).isNull()
- assertThat(cursorPreviewsRepository.previewsModel.value!!.loadMoreRight).isNull()
- assertThat(cursorPreviewsRepository.previewsModel.value!!.previewModels)
- .containsExactly(
- PreviewModel(
- uri = Uri.fromParts("scheme0", "ssp0", "fragment0"),
- mimeType = "image/bitmap"
- ),
- PreviewModel(
- uri = Uri.fromParts("scheme1", "ssp1", "fragment1"),
- mimeType = "image/bitmap"
- ),
- PreviewModel(
- uri = Uri.fromParts("scheme2", "ssp2", "fragment2"),
- mimeType = "image/bitmap"
- ),
- PreviewModel(
- uri = Uri.fromParts("scheme3", "ssp3", "fragment3"),
- mimeType = "image/bitmap"
- ),
- )
- .inOrder()
- }
+ assertThat(cursorPreviewsRepository.previewsModel.value).isNotNull()
+ assertThat(cursorPreviewsRepository.previewsModel.value!!.startIdx).isEqualTo(0)
+ assertThat(cursorPreviewsRepository.previewsModel.value!!.loadMoreLeft).isNull()
+ assertThat(cursorPreviewsRepository.previewsModel.value!!.loadMoreRight).isNull()
+ assertThat(cursorPreviewsRepository.previewsModel.value!!.previewModels)
+ .containsExactly(
+ PreviewModel(
+ uri = Uri.fromParts("scheme0", "ssp0", "fragment0"),
+ mimeType = "image/bitmap",
+ aspectRatio = 2f,
+ ),
+ PreviewModel(
+ uri = Uri.fromParts("scheme1", "ssp1", "fragment1"),
+ mimeType = "image/bitmap"
+ ),
+ PreviewModel(
+ uri = Uri.fromParts("scheme2", "ssp2", "fragment2"),
+ mimeType = "image/bitmap"
+ ),
+ PreviewModel(
+ uri = Uri.fromParts("scheme3", "ssp3", "fragment3"),
+ mimeType = "image/bitmap",
+ aspectRatio = 4f,
+ ),
+ )
+ .inOrder()
+ }
@Test
fun loadMoreLeft_evictRight() =
@@ -294,3 +321,7 @@ class CursorPreviewsInteractorTest {
assertThat(cursorPreviewsRepository.previewsModel.value!!.loadMoreLeft).isNull()
}
}
+
+private fun uri(index: Int) = Uri.fromParts("scheme$index", "ssp$index", "fragment$index")
+
+private infix fun Int.x(height: Int) = Size(this, height)
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 c9f71f49..735bcb1d 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
@@ -20,6 +20,7 @@ package com.android.intentresolver.contentpreview.payloadtoggle.domain.interacto
import android.database.MatrixCursor
import android.net.Uri
+import android.util.Size
import androidx.core.os.bundleOf
import com.android.intentresolver.contentpreview.FileInfo
import com.android.intentresolver.contentpreview.UriMetadataReader
@@ -53,17 +54,26 @@ class FetchPreviewsInteractorTest {
cursorStartPosition: Int = cursor.count() / 2,
pageSize: Int = 16,
maxLoadedPages: Int = 3,
+ previewSizes: Map<Int, Size> = emptyMap(),
block: KosmosTestScope.() -> Unit,
) {
+ val previewUriToSize = previewSizes.mapKeys { uri(it.key) }
with(Kosmos()) {
fakeCursorResolver =
FakeCursorResolver(cursorRange = cursor, cursorStartPosition = cursorStartPosition)
payloadToggleCursorResolver = fakeCursorResolver
contentUris = initialSelection.map { uri(it) }
this.focusedItemIndex = focusedItemIndex
- uriMetadataReader = UriMetadataReader {
- FileInfo.Builder(it).withPreviewUri(it).withMimeType("image/bitmap").build()
- }
+ uriMetadataReader =
+ object : UriMetadataReader {
+ override fun getMetadata(uri: Uri): FileInfo =
+ FileInfo.Builder(uri)
+ .withPreviewUri(uri)
+ .withMimeType("image/bitmap")
+ .build()
+
+ override fun readPreviewSize(uri: Uri): Size? = previewUriToSize[uri]
+ }
this.pageSize = pageSize
this.maxLoadedPages = maxLoadedPages
runKosmosTest { block() }
@@ -94,30 +104,32 @@ class FetchPreviewsInteractorTest {
}
@Test
- fun setsInitialPreviews() = runTest {
- backgroundScope.launch { fetchPreviewsInteractor.activate() }
- runCurrent()
+ fun setsInitialPreviews() =
+ runTest(previewSizes = mapOf(1 to Size(100, 50))) {
+ backgroundScope.launch { fetchPreviewsInteractor.activate() }
+ runCurrent()
- assertThat(cursorPreviewsRepository.previewsModel.value)
- .isEqualTo(
- PreviewsModel(
- previewModels =
- setOf(
- PreviewModel(
- uri = Uri.fromParts("scheme1", "ssp1", "fragment1"),
- mimeType = "image/bitmap",
- ),
- PreviewModel(
- uri = Uri.fromParts("scheme2", "ssp2", "fragment2"),
- mimeType = "image/bitmap",
+ assertThat(cursorPreviewsRepository.previewsModel.value)
+ .isEqualTo(
+ PreviewsModel(
+ previewModels =
+ setOf(
+ PreviewModel(
+ uri = Uri.fromParts("scheme1", "ssp1", "fragment1"),
+ mimeType = "image/bitmap",
+ aspectRatio = 2f
+ ),
+ PreviewModel(
+ uri = Uri.fromParts("scheme2", "ssp2", "fragment2"),
+ mimeType = "image/bitmap",
+ ),
),
- ),
- startIdx = 1,
- loadMoreLeft = null,
- loadMoreRight = null,
+ startIdx = 1,
+ loadMoreLeft = null,
+ loadMoreRight = null,
+ )
)
- )
- }
+ }
@Test
fun lookupCursorFromContentResolver() = runTest {