diff options
Diffstat (limited to 'java')
5 files changed, 90 insertions, 29 deletions
diff --git a/java/src/com/android/intentresolver/contentpreview/UriMetadataHelpers.kt b/java/src/com/android/intentresolver/contentpreview/UriMetadataHelpers.kt index 41638b1f..c532b9a5 100644 --- a/java/src/com/android/intentresolver/contentpreview/UriMetadataHelpers.kt +++ b/java/src/com/android/intentresolver/contentpreview/UriMetadataHelpers.kt @@ -23,9 +23,12 @@ import android.net.Uri import android.provider.DocumentsContract import android.provider.DocumentsContract.Document.FLAG_SUPPORTS_THUMBNAIL import android.provider.Downloads +import android.provider.MediaStore.MediaColumns.HEIGHT +import android.provider.MediaStore.MediaColumns.WIDTH import android.provider.OpenableColumns import android.text.TextUtils import android.util.Log +import android.util.Size import com.android.intentresolver.measurements.runTracing internal fun ContentInterface.getTypeSafe(uri: Uri): String? = @@ -83,6 +86,25 @@ internal fun Cursor.readPreviewUri(): Uri? = } .getOrNull() +fun Cursor.readSize(): Size? { + val widthIdx = columnNames.indexOf(WIDTH) + val heightIdx = columnNames.indexOf(HEIGHT) + return if (widthIdx < 0 || heightIdx < 0 || isNull(widthIdx) || isNull(heightIdx)) { + null + } else { + runCatching { + val width = getInt(widthIdx) + val height = getInt(heightIdx) + if (width >= 0 && height > 0) { + Size(width, height) + } else { + null + } + } + .getOrNull() + } +} + internal fun Cursor.readTitle(): String = runCatching { var nameColIndex = -1 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 3cf2af13..d9612696 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 @@ -16,11 +16,14 @@ package com.android.intentresolver.contentpreview.payloadtoggle.domain.cursor -import android.content.ContentResolver +import android.content.ContentInterface import android.content.Intent +import android.database.Cursor import android.net.Uri import android.service.chooser.AdditionalContentContract.Columns.URI import androidx.core.os.bundleOf +import com.android.intentresolver.contentpreview.payloadtoggle.domain.model.CursorRow +import com.android.intentresolver.contentpreview.readSize import com.android.intentresolver.inject.AdditionalContent import com.android.intentresolver.inject.ChooserIntent import com.android.intentresolver.util.cursor.CursorView @@ -37,23 +40,31 @@ import javax.inject.Qualifier class PayloadToggleCursorResolver @Inject constructor( - private val contentResolver: ContentResolver, + private val contentResolver: ContentInterface, @AdditionalContent private val cursorUri: Uri, @ChooserIntent private val chooserIntent: Intent, -) : CursorResolver<Uri?> { - override suspend fun getCursor(): CursorView<Uri?>? = withCancellationSignal { signal -> +) : CursorResolver<CursorRow?> { + override suspend fun getCursor(): CursorView<CursorRow?>? = withCancellationSignal { signal -> runCatching { contentResolver.query( cursorUri, - arrayOf(URI), + // TODO: uncomment to start using that data + arrayOf(URI /*, WIDTH, HEIGHT*/), bundleOf(Intent.EXTRA_INTENT to chooserIntent), signal, ) } .getOrNull() - ?.viewBy { - getString(0)?.let(Uri::parse)?.takeIf { it.authority != cursorUri.authority } + ?.viewBy { readUri()?.let { uri -> CursorRow(uri, readSize()) } } + } + + private fun Cursor.readUri(): Uri? { + val uriIdx = columnNames.indexOf(URI) + if (uriIdx < 0) return null + return runCatching { + getString(uriIdx)?.let(Uri::parse)?.takeIf { it.authority != cursorUri.authority } } + .getOrNull() } @Module @@ -61,7 +72,7 @@ constructor( interface Binding { @Binds @PayloadToggle - fun bind(cursorResolver: PayloadToggleCursorResolver): CursorResolver<Uri?> + fun bind(cursorResolver: PayloadToggleCursorResolver): CursorResolver<CursorRow?> } } 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 f642f420..9d62ffa2 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 @@ -21,6 +21,7 @@ package com.android.intentresolver.contentpreview.payloadtoggle.domain.interacto import android.net.Uri import android.service.chooser.AdditionalContentContract.CursorExtraKeys.POSITION import com.android.intentresolver.contentpreview.UriMetadataReader +import com.android.intentresolver.contentpreview.payloadtoggle.domain.model.CursorRow import com.android.intentresolver.contentpreview.payloadtoggle.domain.model.LoadDirection import com.android.intentresolver.contentpreview.payloadtoggle.domain.model.LoadedWindow import com.android.intentresolver.contentpreview.payloadtoggle.domain.model.expandWindowLeft @@ -64,7 +65,7 @@ constructor( } /** Start reading data from [uriCursor], and listen for requests to load more. */ - suspend fun launch(uriCursor: CursorView<Uri?>, initialPreviews: Iterable<PreviewModel>) { + suspend fun launch(uriCursor: CursorView<CursorRow?>, initialPreviews: Iterable<PreviewModel>) { // Unclaimed values from the initial selection set. Entries will be removed as the cursor is // read, and any still present are inserted at the start / end of the cursor when it is // reached by the user. @@ -73,7 +74,7 @@ constructor( .asSequence() .mapIndexed { i, m -> Pair(m.uri, Pair(i, m)) } .toMap(ConcurrentHashMap()) - val pagedCursor: PagedCursor<Uri?> = uriCursor.paged(pageSize) + val pagedCursor: PagedCursor<CursorRow?> = uriCursor.paged(pageSize) val startPosition = uriCursor.extras?.getInt(POSITION, 0) ?: 0 val state = readInitialState(pagedCursor, startPosition, unclaimedRecords) processLoadRequests(state, pagedCursor, unclaimedRecords) @@ -82,7 +83,7 @@ constructor( /** Loop forever, processing any loading requests from the UI and updating local cache. */ private suspend fun processLoadRequests( initialState: CursorWindow, - pagedCursor: PagedCursor<Uri?>, + pagedCursor: PagedCursor<CursorRow?>, unclaimedRecords: MutableUnclaimedMap, ) { var state = initialState @@ -108,7 +109,7 @@ constructor( */ private suspend fun Flow<LoadDirection?>.handleOneLoadRequest( state: CursorWindow, - pagedCursor: PagedCursor<Uri?>, + pagedCursor: PagedCursor<CursorRow?>, unclaimedRecords: MutableUnclaimedMap, ): CursorWindow = mapLatest { loadDirection -> @@ -127,7 +128,7 @@ constructor( * [startPosition]. */ private suspend fun readInitialState( - cursor: PagedCursor<Uri?>, + cursor: PagedCursor<CursorRow?>, startPosition: Int, unclaimedRecords: MutableUnclaimedMap, ): CursorWindow { @@ -138,13 +139,13 @@ constructor( if (!hasMoreLeft) { // First read the initial page; this might claim some unclaimed Uris val page = - cursor.getPageUris(startPageIdx)?.toPage(mutableMapOf(), unclaimedRecords) + cursor.getPageRows(startPageIdx)?.toPage(mutableMapOf(), unclaimedRecords) // Now that unclaimed Uris are up-to-date, add them first. putAllUnclaimedLeft(unclaimedRecords) // Then add the loaded page page?.let(::putAll) } else { - cursor.getPageUris(startPageIdx)?.toPage(this, unclaimedRecords) + cursor.getPageRows(startPageIdx)?.toPage(this, unclaimedRecords) } // Finally, add the remainder of the unclaimed Uris. if (!hasMoreRight) { @@ -162,7 +163,7 @@ constructor( } private suspend fun CursorWindow.loadMoreRight( - cursor: PagedCursor<Uri?>, + cursor: PagedCursor<CursorRow?>, unclaimedRecords: MutableUnclaimedMap, ): CursorWindow { val pageNum = lastLoadedPageNum + 1 @@ -181,7 +182,7 @@ constructor( } private suspend fun CursorWindow.loadMoreLeft( - cursor: PagedCursor<Uri?>, + cursor: PagedCursor<CursorRow?>, unclaimedRecords: MutableUnclaimedMap, ): CursorWindow { val pageNum = firstLoadedPageNum - 1 @@ -207,7 +208,7 @@ constructor( private suspend fun readPage( state: CursorWindow, - pagedCursor: PagedCursor<Uri?>, + pagedCursor: PagedCursor<CursorRow?>, pageNum: Int, unclaimedRecords: MutableUnclaimedMap, ): PreviewMap = @@ -216,30 +217,33 @@ constructor( private suspend fun <M : MutablePreviewMap> M.readAndPutPage( state: CursorWindow, - pagedCursor: PagedCursor<Uri?>, + pagedCursor: PagedCursor<CursorRow?>, pageNum: Int, unclaimedRecords: MutableUnclaimedMap, ): M = pagedCursor - .getPageUris(pageNum) // TODO: what do we do if the load fails? - ?.filter { it !in state.merged } + .getPageRows(pageNum) // TODO: what do we do if the load fails? + ?.filter { it.uri !in state.merged } ?.toPage(this, unclaimedRecords) ?: this - private suspend fun <M : MutablePreviewMap> Sequence<Uri>.toPage( + private suspend fun <M : MutablePreviewMap> Sequence<CursorRow>.toPage( destination: M, unclaimedRecords: MutableUnclaimedMap, ): M = // Restrict parallelism so as to not overload the metadata reader; anecdotally, too // many parallel queries causes failures. - mapParallel(parallelism = 4) { uri -> createPreviewModel(uri, unclaimedRecords) } + mapParallel(parallelism = 4) { row -> createPreviewModel(row, unclaimedRecords) } .associateByTo(destination) { it.uri } - private fun createPreviewModel(uri: Uri, unclaimedRecords: MutableUnclaimedMap): PreviewModel = - unclaimedRecords.remove(uri)?.second + private fun createPreviewModel( + row: CursorRow, + unclaimedRecords: MutableUnclaimedMap, + ): PreviewModel = + unclaimedRecords.remove(row.uri)?.second ?: PreviewModel( - uri = uri, - mimeType = uriMetadataReader.getMetadata(uri).mimeType, + uri = row.uri, + mimeType = uriMetadataReader.getMetadata(row.uri).mimeType, ) private fun <M : MutablePreviewMap> M.putAllUnclaimedRight(unclaimed: UnclaimedMap): M = @@ -275,7 +279,7 @@ private fun <M : MutablePreviewMap> M.putAllUnclaimedWhere( .map { it.key to it.value.second } .toMap(this) -private fun PagedCursor<Uri?>.getPageUris(pageNum: Int): Sequence<Uri>? = +private fun PagedCursor<CursorRow?>.getPageRows(pageNum: Int): Sequence<CursorRow>? = get(pageNum)?.filterNotNull() @Qualifier @MustBeDocumented @Retention(AnnotationRetention.RUNTIME) annotation class PageSize 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 9bc7ae63..927a3a84 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 @@ -21,6 +21,7 @@ import com.android.intentresolver.contentpreview.UriMetadataReader import com.android.intentresolver.contentpreview.payloadtoggle.data.repository.PreviewSelectionsRepository import com.android.intentresolver.contentpreview.payloadtoggle.domain.cursor.CursorResolver import com.android.intentresolver.contentpreview.payloadtoggle.domain.cursor.PayloadToggle +import com.android.intentresolver.contentpreview.payloadtoggle.domain.model.CursorRow import com.android.intentresolver.contentpreview.payloadtoggle.shared.model.PreviewModel import com.android.intentresolver.inject.ContentUris import com.android.intentresolver.inject.FocusedItemIndex @@ -39,7 +40,7 @@ constructor( @FocusedItemIndex private val focusedItemIdx: Int, @ContentUris private val selectedItems: List<@JvmSuppressWildcards Uri>, private val uriMetadataReader: UriMetadataReader, - @PayloadToggle private val cursorResolver: CursorResolver<@JvmSuppressWildcards Uri?>, + @PayloadToggle private val cursorResolver: CursorResolver<@JvmSuppressWildcards CursorRow?>, ) { suspend fun activate() = coroutineScope { val cursor = async { cursorResolver.getCursor() } 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 new file mode 100644 index 00000000..f1d856ac --- /dev/null +++ b/java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/model/CursorRow.kt @@ -0,0 +1,23 @@ +/* + * 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.model + +import android.net.Uri +import android.util.Size + +/** Represents additional content cursor row */ +data class CursorRow(val uri: Uri, val previewSize: Size?) |