1 package com.android.sharetest
2 
3 import android.content.ContentProvider
4 import android.content.ContentValues
5 import android.content.res.AssetFileDescriptor
6 import android.database.Cursor
7 import android.database.MatrixCursor
8 import android.net.Uri
9 import android.os.Bundle
10 import android.os.ParcelFileDescriptor
11 import android.os.SystemClock
12 import android.provider.MediaStore
13 import android.util.Size
14 
15 class ImageContentProvider : ContentProvider() {
onCreatenull16     override fun onCreate(): Boolean = true
17 
18     override fun query(
19         uri: Uri,
20         projection: Array<String>?,
21         selection: String?,
22         selectionArgs: Array<String>?,
23         sortOrder: String?,
24     ): Cursor? {
25         SystemClock.sleep(getLatencyMs(queryLatency))
26         val includeSize = runCatching {
27             uri.getQueryParameter(PARAM_SIZE_META).toBoolean()
28         }.getOrDefault(true)
29         if (!includeSize) return null
30         val size = getImageSize(uri) ?: return null
31         return MatrixCursor(
32             arrayOf(
33                 MediaStore.MediaColumns.WIDTH,
34                 MediaStore.MediaColumns.HEIGHT
35             )
36         ).apply {
37             addRow(arrayOf(size.width, size.height))
38         }
39     }
40 
getTypenull41     override fun getType(uri: Uri): String? {
42         Thread.sleep(getLatencyMs(getTypeLatency))
43         return uri.getQueryParameter(PARAM_TYPE) ?: getTypeFromUri(uri)
44     }
45 
getStreamTypesnull46     override fun getStreamTypes(uri: Uri, mimeTypeFilter: String): Array<String>? {
47         val list = ArrayList<String>(2)
48         uri.getQueryParameter(PARAM_TYPE)?.let { list.add(it) }
49         getTypeFromUri(uri)?.let { list.add(it) }
50         return if (list.isEmpty()) {
51             super.getStreamTypes(uri, mimeTypeFilter)
52         } else {
53             list.toArray(emptyArray())
54         }
55     }
56 
getTypeFromUrinull57     private fun getTypeFromUri(uri: Uri): String? {
58         if (uri.lastPathSegment?.endsWith("png", true) == true) {
59             return "image/png"
60         }
61         if (uri.lastPathSegment?.endsWith("jpg", true) == true) {
62             return "image/jpg"
63         }
64         return null
65     }
66 
insertnull67     override fun insert(uri: Uri, values: ContentValues?): Uri? = null
68 
69     override fun delete(uri: Uri, selection: String?, selectionArgs: Array<String>?): Int = 0
70 
71     override fun update(
72         uri: Uri,
73         values: ContentValues?,
74         selection: String?,
75         selectionArgs: Array<String>?,
76     ): Int = 0
77 
78     override fun openTypedAssetFile(
79         uri: Uri,
80         mimeTypeFilter: String,
81         opts: Bundle?,
82     ): AssetFileDescriptor? {
83         return openAssetFile(uri, "r")
84     }
85 
openAssetFilenull86     override fun openAssetFile(uri: Uri, mode: String): AssetFileDescriptor? {
87         Thread.sleep(getLatencyMs(openLatency))
88 
89         if (shouldFailOpen()) {
90             return null
91         }
92 
93         return uri.lastPathSegment?.let { context?.assets?.openFd(it) }
94     }
95 
openFilenull96     override fun openFile(uri: Uri, mode: String): ParcelFileDescriptor? =
97         openAssetFile(uri, mode)?.parcelFileDescriptor
98 
99     private fun shouldFailOpen() = getRandomFailure(openFailureRate)
100 
101     companion object {
102         private val sizeMap = mapOf(
103             "img1.jpg" to Size(1944, 2592),
104             "img2.jpg" to Size(2368, 3200),
105             "img3.jpg" to Size(3200, 2368),
106             "img4.jpg" to Size(4032, 3024),
107             "img5.jpg" to Size(2448, 3264),
108             "img6.jpg" to Size(4032, 3024),
109             "img7.jpg" to Size(3024, 4032),
110             "img8.jpg" to Size(1600, 1200),
111         )
112 
113         fun makeItemUri(idx: Int, mimeType: String, includeSize: Boolean): Uri =
114             Uri.parse("${URI_PREFIX}img${(idx % IMAGE_COUNT) + 1}.jpg")
115                 .buildUpon()
116                 .appendQueryParameter(PARAM_TYPE, mimeType)
117                 .appendQueryParameter(PARAM_SIZE_META, includeSize.toString())
118                 .appendQueryParameter("index", idx.toString())
119                 .build()
120 
121         fun getImageSize(uri: Uri): Size? {
122             val name = uri.lastPathSegment ?: return null
123             return sizeMap[name]
124         }
125 
126         const val IMAGE_COUNT = 8
127 
128         const val URI_PREFIX = "content://com.android.sharetest.provider/"
129         const val PARAM_TYPE = "type"
130         const val PARAM_SIZE_META = "ismeta"
131         val ICON_URI: Uri = Uri.parse("${URI_PREFIX}letter_a.png")
132 
133         var getTypeLatency = 0L
134         var openLatency = 0L
135         var openFailureRate = 0f
136         var queryLatency = 0L
137     }
138 }
139 
140 
141