summaryrefslogtreecommitdiff
path: root/java
diff options
context:
space:
mode:
author Andrey Yepin <ayepin@google.com> 2024-07-30 10:57:39 -0700
committer Andrey Yepin <ayepin@google.com> 2024-07-30 13:46:26 -0700
commit7da3a9396f38d92dd01f765d99d1c9d3f65a46d0 (patch)
treeecdd22ec0b9c55a288570643df7aad0d6ae82697 /java
parent16ac22b96bda769ff231785cbdf962847a6a7540 (diff)
Do not crash when fail to read from the additional content cursor
Bug: 354546194 Test: atest IntentResolver-tests-unit Flag: EXEMPT bugfix Change-Id: I5123fe6c1cabbbae7479d2cea8bd9e4b8ff0b4a8
Diffstat (limited to 'java')
-rw-r--r--java/src/com/android/intentresolver/contentpreview/payloadtoggle/domain/interactor/CursorPreviewsInteractor.kt73
1 files changed, 51 insertions, 22 deletions
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 a475263c..7d658209 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
@@ -20,6 +20,7 @@ package com.android.intentresolver.contentpreview.payloadtoggle.domain.interacto
import android.net.Uri
import android.service.chooser.AdditionalContentContract.CursorExtraKeys.POSITION
+import android.util.Log
import com.android.intentresolver.contentpreview.UriMetadataReader
import com.android.intentresolver.contentpreview.payloadtoggle.domain.model.CursorRow
import com.android.intentresolver.contentpreview.payloadtoggle.domain.model.LoadDirection
@@ -51,6 +52,8 @@ import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.mapLatest
+private const val TAG = "CursorPreviewsIntr"
+
/** Queries data from a remote cursor, and caches it locally for presentation in Shareousel. */
class CursorPreviewsInteractor
@Inject
@@ -273,8 +276,7 @@ constructor(
pagedCursor
.getPageRows(pageNum) // TODO: what do we do if the load fails?
?.filter { it.uri !in state.merged }
- ?.toPage(this, unclaimedRecords)
- ?: this
+ ?.toPage(this, unclaimedRecords) ?: this
private suspend fun <M : MutablePreviewMap> Sequence<CursorRow>.toPage(
destination: M,
@@ -288,26 +290,32 @@ constructor(
private fun createPreviewModel(
row: CursorRow,
unclaimedRecords: MutableUnclaimedMap,
- ): 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)
+ ): 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 }
@@ -343,7 +351,28 @@ private fun <M : MutablePreviewMap> M.putAllUnclaimedWhere(
.toMap(this)
private fun PagedCursor<CursorRow?>.getPageRows(pageNum: Int): Sequence<CursorRow>? =
- get(pageNum)?.filterNotNull()
+ runCatching { get(pageNum) }
+ .onFailure { Log.e(TAG, "Failed to read additional content cursor page #$pageNum", it) }
+ .getOrNull()
+ ?.asSafeSequence()
+ ?.filterNotNull()
+
+private fun <T> Sequence<T>.asSafeSequence(): Sequence<T> {
+ return if (this is SafeSequence) this else SafeSequence(this)
+}
+
+private class SafeSequence<T>(private val sequence: Sequence<T>) : Sequence<T> {
+ override fun iterator(): Iterator<T> =
+ sequence.iterator().let { if (it is SafeIterator) it else SafeIterator(it) }
+}
+
+private class SafeIterator<T>(private val iterator: Iterator<T>) : Iterator<T> by iterator {
+ override fun hasNext(): Boolean {
+ return runCatching { iterator.hasNext() }
+ .onFailure { Log.e(TAG, "Failed to read cursor", it) }
+ .getOrDefault(false)
+ }
+}
@Qualifier @MustBeDocumented @Retention(AnnotationRetention.RUNTIME) annotation class PageSize