summaryrefslogtreecommitdiff
path: root/java/src
diff options
context:
space:
mode:
author Andrey Epin <ayepin@google.com> 2023-12-01 09:36:53 -0800
committer Andrey Yepin <ayepin@google.com> 2024-10-01 15:37:37 -0700
commitf6400db571602fbbb3c5fa88276c3e5ed40792da (patch)
treed081d49d3d6f10a5d5a61922c05311ba0df29d9f /java/src
parent7e6f54913c7a6cfc5b50d16fe3b0d38a7c1bfe20 (diff)
Retrieve URI title metadata through separate query calls
As a file name accessed independently from preview-related metadata and also asynchronously, make it be retrieved through a separate provider calls. This way an error during preview metadata call won't affect the file name reading. Bug: 365748223 Test: atest IntentResolver-tests-unit Test: manual testing with a test app that shares a MediaProvider video Flag: com.android.intentresolver.individual_metadata_title_read Change-Id: I2e87913149e679fee22e4371d2b42f485c8b04e4
Diffstat (limited to 'java/src')
-rw-r--r--java/src/com/android/intentresolver/contentpreview/PreviewDataProvider.kt69
-rw-r--r--java/src/com/android/intentresolver/contentpreview/UriMetadataHelpers.kt37
-rw-r--r--java/src/com/android/intentresolver/ui/viewmodel/ChooserViewModel.kt1
3 files changed, 54 insertions, 53 deletions
diff --git a/java/src/com/android/intentresolver/contentpreview/PreviewDataProvider.kt b/java/src/com/android/intentresolver/contentpreview/PreviewDataProvider.kt
index 9b2dbebf..07cbaa04 100644
--- a/java/src/com/android/intentresolver/contentpreview/PreviewDataProvider.kt
+++ b/java/src/com/android/intentresolver/contentpreview/PreviewDataProvider.kt
@@ -24,15 +24,16 @@ import android.provider.DocumentsContract
import android.provider.DocumentsContract.Document.FLAG_SUPPORTS_THUMBNAIL
import android.provider.Downloads
import android.provider.OpenableColumns
+import android.service.chooser.Flags.chooserPayloadToggling
import android.text.TextUtils
import android.util.Log
import androidx.annotation.OpenForTesting
import androidx.annotation.VisibleForTesting
+import com.android.intentresolver.Flags.individualMetadataTitleRead
import com.android.intentresolver.contentpreview.ContentPreviewType.CONTENT_PREVIEW_FILE
import com.android.intentresolver.contentpreview.ContentPreviewType.CONTENT_PREVIEW_IMAGE
import com.android.intentresolver.contentpreview.ContentPreviewType.CONTENT_PREVIEW_PAYLOAD_SELECTION
import com.android.intentresolver.contentpreview.ContentPreviewType.CONTENT_PREVIEW_TEXT
-import com.android.intentresolver.inject.ChooserServiceFlags
import com.android.intentresolver.measurements.runTracing
import com.android.intentresolver.util.ownedByCurrentUser
import java.util.concurrent.atomic.AtomicInteger
@@ -55,14 +56,19 @@ import kotlinx.coroutines.withTimeoutOrNull
* A set of metadata columns we read for a content URI (see
* [PreviewDataProvider.UriRecord.readQueryResult] method).
*/
-@VisibleForTesting
-val METADATA_COLUMNS =
+private val METADATA_COLUMNS =
arrayOf(
DocumentsContract.Document.COLUMN_FLAGS,
MediaMetadata.METADATA_KEY_DISPLAY_ICON_URI,
OpenableColumns.DISPLAY_NAME,
- Downloads.Impl.COLUMN_TITLE
+ Downloads.Impl.COLUMN_TITLE,
)
+
+/** Preview-related metadata columns. */
+@VisibleForTesting
+val ICON_METADATA_COLUMNS =
+ arrayOf(DocumentsContract.Document.COLUMN_FLAGS, MediaMetadata.METADATA_KEY_DISPLAY_ICON_URI)
+
private const val TIMEOUT_MS = 1_000L
/**
@@ -77,7 +83,6 @@ constructor(
private val targetIntent: Intent,
private val additionalContentUri: Uri?,
private val contentResolver: ContentInterface,
- private val featureFlags: ChooserServiceFlags,
private val typeClassifier: MimeTypeClassifier = DefaultMimeTypeClassifier,
) {
@@ -128,7 +133,7 @@ constructor(
* IMAGE, FILE, TEXT. */
if (!targetIntent.isSend || records.isEmpty()) {
CONTENT_PREVIEW_TEXT
- } else if (featureFlags.chooserPayloadToggling() && shouldShowPayloadSelection()) {
+ } else if (chooserPayloadToggling() && shouldShowPayloadSelection()) {
// TODO: replace with the proper flags injection
CONTENT_PREVIEW_PAYLOAD_SELECTION
} else {
@@ -141,7 +146,7 @@ constructor(
Log.w(
ContentPreviewUi.TAG,
"An attempt to read preview type from a cancelled scope",
- e
+ e,
)
CONTENT_PREVIEW_FILE
}
@@ -159,7 +164,7 @@ constructor(
Log.w(
ContentPreviewUi.TAG,
"Failed to check URI authorities; no payload toggling",
- it
+ it,
)
}
.getOrDefault(false)
@@ -183,7 +188,7 @@ constructor(
Log.w(
ContentPreviewUi.TAG,
"An attempt to read first file info from a cancelled scope",
- e
+ e,
)
}
builder.build()
@@ -212,14 +217,20 @@ constructor(
if (records.isEmpty()) {
throw IndexOutOfBoundsException("There are no shared URIs")
}
- callerScope.launch {
- val result = scope.async { getFirstFileName() }.await()
- callback.accept(result)
- }
+ callerScope.launch { callback.accept(getFirstFileName()) }
}
+ /**
+ * Returns a title for the first shared URI which is read from URI metadata or, if the metadata
+ * is not provided, derived from the URI.
+ */
@Throws(IndexOutOfBoundsException::class)
- private fun getFirstFileName(): String {
+ suspend fun getFirstFileName(): String {
+ return scope.async { getFirstFileNameInternal() }.await()
+ }
+
+ @Throws(IndexOutOfBoundsException::class)
+ private fun getFirstFileNameInternal(): String {
if (records.isEmpty()) throw IndexOutOfBoundsException("There are no shared URIs")
val record = records[0]
@@ -282,16 +293,23 @@ constructor(
get() = query.supportsThumbnail
val title: String
- get() = query.title
+ get() = if (individualMetadataTitleRead()) titleFromQuery else query.title
val iconUri: Uri?
get() = query.iconUri
- private val query by lazy { readQueryResult() }
+ private val query by lazy {
+ readQueryResult(
+ if (individualMetadataTitleRead()) ICON_METADATA_COLUMNS else METADATA_COLUMNS
+ )
+ }
+
+ private val titleFromQuery by lazy {
+ readDisplayNameFromQuery().takeIf { !TextUtils.isEmpty(it) } ?: readTitleFromQuery()
+ }
- private fun readQueryResult(): QueryResult =
- // TODO: rewrite using methods from UiMetadataHelpers.kt
- contentResolver.querySafe(uri, METADATA_COLUMNS)?.use { cursor ->
+ private fun readQueryResult(columns: Array<String>): QueryResult =
+ contentResolver.querySafe(uri, columns)?.use { cursor ->
if (!cursor.moveToFirst()) return@use null
var flagColIdx = -1
@@ -329,12 +347,23 @@ constructor(
QueryResult(supportsThumbnail, title, iconUri)
} ?: QueryResult()
+
+ private fun readTitleFromQuery(): String = readStringColumn(Downloads.Impl.COLUMN_TITLE)
+
+ private fun readDisplayNameFromQuery(): String =
+ readStringColumn(OpenableColumns.DISPLAY_NAME)
+
+ private fun readStringColumn(column: String): String =
+ contentResolver.querySafe(uri, arrayOf(column))?.use { cursor ->
+ if (!cursor.moveToFirst()) return@use null
+ cursor.readString(column)
+ } ?: ""
}
private class QueryResult(
val supportsThumbnail: Boolean = false,
val title: String = "",
- val iconUri: Uri? = null
+ val iconUri: Uri? = null,
)
}
diff --git a/java/src/com/android/intentresolver/contentpreview/UriMetadataHelpers.kt b/java/src/com/android/intentresolver/contentpreview/UriMetadataHelpers.kt
index c532b9a5..80d0e058 100644
--- a/java/src/com/android/intentresolver/contentpreview/UriMetadataHelpers.kt
+++ b/java/src/com/android/intentresolver/contentpreview/UriMetadataHelpers.kt
@@ -22,11 +22,8 @@ import android.media.MediaMetadata
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
@@ -78,12 +75,7 @@ internal fun Cursor.readSupportsThumbnail(): Boolean =
.getOrDefault(false)
internal fun Cursor.readPreviewUri(): Uri? =
- runCatching {
- columnNames
- .indexOf(MediaMetadata.METADATA_KEY_DISPLAY_ICON_URI)
- .takeIf { it >= 0 }
- ?.let { getString(it)?.let(Uri::parse) }
- }
+ runCatching { readString(MediaMetadata.METADATA_KEY_DISPLAY_ICON_URI)?.let(Uri::parse) }
.getOrNull()
fun Cursor.readSize(): Size? {
@@ -105,34 +97,15 @@ fun Cursor.readSize(): Size? {
}
}
-internal fun Cursor.readTitle(): String =
- runCatching {
- var nameColIndex = -1
- var titleColIndex = -1
- // TODO: double-check why Cursor#getColumnInded didn't work
- columnNames.forEachIndexed { i, columnName ->
- when (columnName) {
- OpenableColumns.DISPLAY_NAME -> nameColIndex = i
- Downloads.Impl.COLUMN_TITLE -> titleColIndex = i
- }
- }
-
- var title = ""
- if (nameColIndex >= 0) {
- title = getString(nameColIndex) ?: ""
- }
- if (TextUtils.isEmpty(title) && titleColIndex >= 0) {
- title = getString(titleColIndex) ?: ""
- }
- title
- }
- .getOrDefault("")
+internal fun Cursor.readString(columnName: String): String? =
+ runCatching { columnNames.indexOf(columnName).takeIf { it >= 0 }?.let { getString(it) } }
+ .getOrNull()
private fun logProviderPermissionWarning(uri: Uri, dataName: String) {
// The ContentResolver already logs the exception. Log something more informative.
Log.w(
ContentPreviewUi.TAG,
"Could not read $uri $dataName. If a preview is desired, call Intent#setClipData() to" +
- " ensure that the sharesheet is given permission."
+ " ensure that the sharesheet is given permission.",
)
}
diff --git a/java/src/com/android/intentresolver/ui/viewmodel/ChooserViewModel.kt b/java/src/com/android/intentresolver/ui/viewmodel/ChooserViewModel.kt
index e6f12750..fe7e9109 100644
--- a/java/src/com/android/intentresolver/ui/viewmodel/ChooserViewModel.kt
+++ b/java/src/com/android/intentresolver/ui/viewmodel/ChooserViewModel.kt
@@ -95,7 +95,6 @@ constructor(
chooserRequest.targetIntent,
chooserRequest.additionalContentUri,
contentResolver,
- flags,
)
}