• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * 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.MediaStore.MediaColumns.HEIGHT
26 import android.provider.MediaStore.MediaColumns.WIDTH
27 import android.util.Log
28 import android.util.Size
29 import com.android.intentresolver.measurements.runTracing
30 
getTypeSafenull31 internal fun ContentInterface.getTypeSafe(uri: Uri): String? =
32     runTracing("getType") {
33         try {
34             getType(uri)
35         } catch (e: SecurityException) {
36             logProviderPermissionWarning(uri, "mime type")
37             null
38         } catch (t: Throwable) {
39             Log.e(ContentPreviewUi.TAG, "Failed to read metadata, uri: $uri", t)
40             null
41         }
42     }
43 
getStreamTypesSafenull44 internal fun ContentInterface.getStreamTypesSafe(uri: Uri): Array<String?> =
45     runTracing("getStreamTypes") {
46         try {
47             getStreamTypes(uri, "*/*") ?: emptyArray()
48         } catch (e: SecurityException) {
49             logProviderPermissionWarning(uri, "stream types")
50             emptyArray<String?>()
51         } catch (t: Throwable) {
52             Log.e(ContentPreviewUi.TAG, "Failed to read stream types, uri: $uri", t)
53             emptyArray<String?>()
54         }
55     }
56 
querySafenull57 internal fun ContentInterface.querySafe(uri: Uri, columns: Array<String>): Cursor? =
58     runTracing("query") {
59         try {
60             query(uri, columns, null, null)
61         } catch (e: SecurityException) {
62             logProviderPermissionWarning(uri, "metadata")
63             null
64         } catch (t: Throwable) {
65             Log.e(ContentPreviewUi.TAG, "Failed to read metadata, uri: $uri", t)
66             null
67         }
68     }
69 
readSupportsThumbnailnull70 internal fun Cursor.readSupportsThumbnail(): Boolean =
71     runCatching {
72             val flagColIdx = columnNames.indexOf(DocumentsContract.Document.COLUMN_FLAGS)
73             flagColIdx >= 0 && ((getInt(flagColIdx) and FLAG_SUPPORTS_THUMBNAIL) != 0)
74         }
75         .getOrDefault(false)
76 
readPreviewUrinull77 internal fun Cursor.readPreviewUri(): Uri? =
78     runCatching { readString(MediaMetadata.METADATA_KEY_DISPLAY_ICON_URI)?.let(Uri::parse) }
79         .getOrNull()
80 
Cursornull81 fun Cursor.readSize(): Size? {
82     val widthIdx = columnNames.indexOf(WIDTH)
83     val heightIdx = columnNames.indexOf(HEIGHT)
84     return if (widthIdx < 0 || heightIdx < 0 || isNull(widthIdx) || isNull(heightIdx)) {
85         null
86     } else {
87         runCatching {
88                 val width = getInt(widthIdx)
89                 val height = getInt(heightIdx)
90                 if (width >= 0 && height > 0) {
91                     Size(width, height)
92                 } else {
93                     null
94                 }
95             }
96             .getOrNull()
97     }
98 }
99 
readStringnull100 internal fun Cursor.readString(columnName: String): String? =
101     runCatching { columnNames.indexOf(columnName).takeIf { it >= 0 }?.let { getString(it) } }
102         .getOrNull()
103 
logProviderPermissionWarningnull104 private fun logProviderPermissionWarning(uri: Uri, dataName: String) {
105     // The ContentResolver already logs the exception. Log something more informative.
106     Log.w(
107         ContentPreviewUi.TAG,
108         "Could not read $uri $dataName. If a preview is desired, call Intent#setClipData() to" +
109             " ensure that the sharesheet is given permission.",
110     )
111 }
112