1 /*
<lambda>null2 * Copyright 2024 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 package com.android.intentresolver.contentpreview
18
19 import android.content.ContentInterface
20 import android.database.Cursor
21 import android.media.MediaMetadata
22 import android.net.Uri
23 import android.provider.DocumentsContract
24 import android.provider.DocumentsContract.Document.FLAG_SUPPORTS_THUMBNAIL
25 import android.provider.Downloads
26 import android.provider.MediaStore.MediaColumns.HEIGHT
27 import android.provider.MediaStore.MediaColumns.WIDTH
28 import android.provider.OpenableColumns
29 import android.text.TextUtils
30 import android.util.Log
31 import android.util.Size
32 import com.android.intentresolver.measurements.runTracing
33
34 internal fun ContentInterface.getTypeSafe(uri: Uri): String? =
35 runTracing("getType") {
36 try {
37 getType(uri)
38 } catch (e: SecurityException) {
39 logProviderPermissionWarning(uri, "mime type")
40 null
41 } catch (t: Throwable) {
42 Log.e(ContentPreviewUi.TAG, "Failed to read metadata, uri: $uri", t)
43 null
44 }
45 }
46
getStreamTypesSafenull47 internal fun ContentInterface.getStreamTypesSafe(uri: Uri): Array<String?> =
48 runTracing("getStreamTypes") {
49 try {
50 getStreamTypes(uri, "*/*") ?: emptyArray()
51 } catch (e: SecurityException) {
52 logProviderPermissionWarning(uri, "stream types")
53 emptyArray<String?>()
54 } catch (t: Throwable) {
55 Log.e(ContentPreviewUi.TAG, "Failed to read stream types, uri: $uri", t)
56 emptyArray<String?>()
57 }
58 }
59
querySafenull60 internal fun ContentInterface.querySafe(uri: Uri, columns: Array<String>): Cursor? =
61 runTracing("query") {
62 try {
63 query(uri, columns, null, null)
64 } catch (e: SecurityException) {
65 logProviderPermissionWarning(uri, "metadata")
66 null
67 } catch (t: Throwable) {
68 Log.e(ContentPreviewUi.TAG, "Failed to read metadata, uri: $uri", t)
69 null
70 }
71 }
72
readSupportsThumbnailnull73 internal fun Cursor.readSupportsThumbnail(): Boolean =
74 runCatching {
75 val flagColIdx = columnNames.indexOf(DocumentsContract.Document.COLUMN_FLAGS)
76 flagColIdx >= 0 && ((getInt(flagColIdx) and FLAG_SUPPORTS_THUMBNAIL) != 0)
77 }
78 .getOrDefault(false)
79
readPreviewUrinull80 internal fun Cursor.readPreviewUri(): Uri? =
81 runCatching {
82 columnNames
83 .indexOf(MediaMetadata.METADATA_KEY_DISPLAY_ICON_URI)
84 .takeIf { it >= 0 }
85 ?.let { getString(it)?.let(Uri::parse) }
86 }
87 .getOrNull()
88
Cursornull89 fun Cursor.readSize(): Size? {
90 val widthIdx = columnNames.indexOf(WIDTH)
91 val heightIdx = columnNames.indexOf(HEIGHT)
92 return if (widthIdx < 0 || heightIdx < 0 || isNull(widthIdx) || isNull(heightIdx)) {
93 null
94 } else {
95 runCatching {
96 val width = getInt(widthIdx)
97 val height = getInt(heightIdx)
98 if (width >= 0 && height > 0) {
99 Size(width, height)
100 } else {
101 null
102 }
103 }
104 .getOrNull()
105 }
106 }
107
readTitlenull108 internal fun Cursor.readTitle(): String =
109 runCatching {
110 var nameColIndex = -1
111 var titleColIndex = -1
112 // TODO: double-check why Cursor#getColumnInded didn't work
113 columnNames.forEachIndexed { i, columnName ->
114 when (columnName) {
115 OpenableColumns.DISPLAY_NAME -> nameColIndex = i
116 Downloads.Impl.COLUMN_TITLE -> titleColIndex = i
117 }
118 }
119
120 var title = ""
121 if (nameColIndex >= 0) {
122 title = getString(nameColIndex) ?: ""
123 }
124 if (TextUtils.isEmpty(title) && titleColIndex >= 0) {
125 title = getString(titleColIndex) ?: ""
126 }
127 title
128 }
129 .getOrDefault("")
130
logProviderPermissionWarningnull131 private fun logProviderPermissionWarning(uri: Uri, dataName: String) {
132 // The ContentResolver already logs the exception. Log something more informative.
133 Log.w(
134 ContentPreviewUi.TAG,
135 "Could not read $uri $dataName. If a preview is desired, call Intent#setClipData() to" +
136 " ensure that the sharesheet is given permission."
137 )
138 }
139