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