expo-media-library 18.3.0-canary-20251023-4c86f95 → 18.3.0-canary-20251031-b135dff
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +3 -0
- package/android/build.gradle +2 -2
- package/android/src/main/java/expo/modules/medialibrary/next/MediaLibraryNextModule.kt +4 -26
- package/android/src/main/java/expo/modules/medialibrary/next/exceptions/PermissionExceptions.kt +1 -2
- package/android/src/main/java/expo/modules/medialibrary/next/extensions/ContextExtensions.kt +16 -0
- package/android/src/main/java/expo/modules/medialibrary/next/extensions/resolver/QueryOne.kt +1 -2
- package/android/src/main/java/expo/modules/medialibrary/next/extensions/resolver/SafeQuery.kt +20 -0
- package/android/src/main/java/expo/modules/medialibrary/next/objects/album/factories/AlbumModernFactory.kt +3 -0
- package/android/src/main/java/expo/modules/medialibrary/next/objects/asset/delegates/AssetLegacyDelegate.kt +12 -6
- package/android/src/main/java/expo/modules/medialibrary/next/objects/asset/delegates/AssetModernDelegate.kt +8 -7
- package/android/src/main/java/expo/modules/medialibrary/next/objects/asset/deleters/AssetLegacyDeleter.kt +7 -2
- package/android/src/main/java/expo/modules/medialibrary/next/objects/asset/factories/AssetLegacyFactory.kt +29 -14
- package/android/src/main/java/expo/modules/medialibrary/next/objects/asset/factories/AssetModernFactory.kt +3 -0
- package/android/src/main/java/expo/modules/medialibrary/next/objects/query/builder/QueryLegacyExecutor.kt +2 -1
- package/android/src/main/java/expo/modules/medialibrary/next/permissions/MediaStorePermissionsDelegate.kt +5 -11
- package/android/src/main/java/expo/modules/medialibrary/next/permissions/SystemPermissionsDelegate.kt +16 -39
- package/build/MediaLibrary.d.ts +4 -2
- package/build/MediaLibrary.d.ts.map +1 -1
- package/build/MediaLibrary.js.map +1 -1
- package/expo-module.config.json +1 -1
- package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/{18.3.0-canary-20251023-4c86f95/expo.modules.medialibrary-18.3.0-canary-20251023-4c86f95-sources.jar → 18.3.0-canary-20251031-b135dff/expo.modules.medialibrary-18.3.0-canary-20251031-b135dff-sources.jar} +0 -0
- package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.3.0-canary-20251031-b135dff/expo.modules.medialibrary-18.3.0-canary-20251031-b135dff-sources.jar.md5 +1 -0
- package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.3.0-canary-20251031-b135dff/expo.modules.medialibrary-18.3.0-canary-20251031-b135dff-sources.jar.sha1 +1 -0
- package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.3.0-canary-20251031-b135dff/expo.modules.medialibrary-18.3.0-canary-20251031-b135dff-sources.jar.sha256 +1 -0
- package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.3.0-canary-20251031-b135dff/expo.modules.medialibrary-18.3.0-canary-20251031-b135dff-sources.jar.sha512 +1 -0
- package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.3.0-canary-20251031-b135dff/expo.modules.medialibrary-18.3.0-canary-20251031-b135dff.aar +0 -0
- package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.3.0-canary-20251031-b135dff/expo.modules.medialibrary-18.3.0-canary-20251031-b135dff.aar.md5 +1 -0
- package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.3.0-canary-20251031-b135dff/expo.modules.medialibrary-18.3.0-canary-20251031-b135dff.aar.sha1 +1 -0
- package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.3.0-canary-20251031-b135dff/expo.modules.medialibrary-18.3.0-canary-20251031-b135dff.aar.sha256 +1 -0
- package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.3.0-canary-20251031-b135dff/expo.modules.medialibrary-18.3.0-canary-20251031-b135dff.aar.sha512 +1 -0
- package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/{18.3.0-canary-20251023-4c86f95/expo.modules.medialibrary-18.3.0-canary-20251023-4c86f95.module → 18.3.0-canary-20251031-b135dff/expo.modules.medialibrary-18.3.0-canary-20251031-b135dff.module} +22 -22
- package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.3.0-canary-20251031-b135dff/expo.modules.medialibrary-18.3.0-canary-20251031-b135dff.module.md5 +1 -0
- package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.3.0-canary-20251031-b135dff/expo.modules.medialibrary-18.3.0-canary-20251031-b135dff.module.sha1 +1 -0
- package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.3.0-canary-20251031-b135dff/expo.modules.medialibrary-18.3.0-canary-20251031-b135dff.module.sha256 +1 -0
- package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.3.0-canary-20251031-b135dff/expo.modules.medialibrary-18.3.0-canary-20251031-b135dff.module.sha512 +1 -0
- package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/{18.3.0-canary-20251023-4c86f95/expo.modules.medialibrary-18.3.0-canary-20251023-4c86f95.pom → 18.3.0-canary-20251031-b135dff/expo.modules.medialibrary-18.3.0-canary-20251031-b135dff.pom} +1 -1
- package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.3.0-canary-20251031-b135dff/expo.modules.medialibrary-18.3.0-canary-20251031-b135dff.pom.md5 +1 -0
- package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.3.0-canary-20251031-b135dff/expo.modules.medialibrary-18.3.0-canary-20251031-b135dff.pom.sha1 +1 -0
- package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.3.0-canary-20251031-b135dff/expo.modules.medialibrary-18.3.0-canary-20251031-b135dff.pom.sha256 +1 -0
- package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.3.0-canary-20251031-b135dff/expo.modules.medialibrary-18.3.0-canary-20251031-b135dff.pom.sha512 +1 -0
- package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/maven-metadata.xml +4 -4
- package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/maven-metadata.xml.md5 +1 -1
- package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/maven-metadata.xml.sha1 +1 -1
- package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/maven-metadata.xml.sha256 +1 -1
- package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/maven-metadata.xml.sha512 +1 -1
- package/package.json +3 -3
- package/src/MediaLibrary.ts +4 -2
- package/ios/Tests/MediaLibrarySpec.swift +0 -116
- package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.3.0-canary-20251023-4c86f95/expo.modules.medialibrary-18.3.0-canary-20251023-4c86f95-sources.jar.md5 +0 -1
- package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.3.0-canary-20251023-4c86f95/expo.modules.medialibrary-18.3.0-canary-20251023-4c86f95-sources.jar.sha1 +0 -1
- package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.3.0-canary-20251023-4c86f95/expo.modules.medialibrary-18.3.0-canary-20251023-4c86f95-sources.jar.sha256 +0 -1
- package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.3.0-canary-20251023-4c86f95/expo.modules.medialibrary-18.3.0-canary-20251023-4c86f95-sources.jar.sha512 +0 -1
- package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.3.0-canary-20251023-4c86f95/expo.modules.medialibrary-18.3.0-canary-20251023-4c86f95.aar +0 -0
- package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.3.0-canary-20251023-4c86f95/expo.modules.medialibrary-18.3.0-canary-20251023-4c86f95.aar.md5 +0 -1
- package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.3.0-canary-20251023-4c86f95/expo.modules.medialibrary-18.3.0-canary-20251023-4c86f95.aar.sha1 +0 -1
- package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.3.0-canary-20251023-4c86f95/expo.modules.medialibrary-18.3.0-canary-20251023-4c86f95.aar.sha256 +0 -1
- package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.3.0-canary-20251023-4c86f95/expo.modules.medialibrary-18.3.0-canary-20251023-4c86f95.aar.sha512 +0 -1
- package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.3.0-canary-20251023-4c86f95/expo.modules.medialibrary-18.3.0-canary-20251023-4c86f95.module.md5 +0 -1
- package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.3.0-canary-20251023-4c86f95/expo.modules.medialibrary-18.3.0-canary-20251023-4c86f95.module.sha1 +0 -1
- package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.3.0-canary-20251023-4c86f95/expo.modules.medialibrary-18.3.0-canary-20251023-4c86f95.module.sha256 +0 -1
- package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.3.0-canary-20251023-4c86f95/expo.modules.medialibrary-18.3.0-canary-20251023-4c86f95.module.sha512 +0 -1
- package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.3.0-canary-20251023-4c86f95/expo.modules.medialibrary-18.3.0-canary-20251023-4c86f95.pom.md5 +0 -1
- package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.3.0-canary-20251023-4c86f95/expo.modules.medialibrary-18.3.0-canary-20251023-4c86f95.pom.sha1 +0 -1
- package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.3.0-canary-20251023-4c86f95/expo.modules.medialibrary-18.3.0-canary-20251023-4c86f95.pom.sha256 +0 -1
- package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.3.0-canary-20251023-4c86f95/expo.modules.medialibrary-18.3.0-canary-20251023-4c86f95.pom.sha512 +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
|
|
12
12
|
### 🐛 Bug fixes
|
|
13
13
|
|
|
14
|
+
- Fix `endCursor` description in the documentation. ([#39120](https://github.com/expo/expo/pull/39120) by [@Wenszel](https://github.com/Wenszel))
|
|
14
15
|
- [next][iOS] Convert `id` to URI format ([#39920](https://github.com/expo/expo/pull/39920) by [@Wenszel](https://github.com/Wenszel))
|
|
15
16
|
- [next][android] Fix `delete()` throwing security exception ([#39914](https://github.com/expo/expo/pull/39914) by [@Wenszel](https://github.com/Wenszel))
|
|
16
17
|
- [next][android] Change default root directory to Pictures ([#39716](https://github.com/expo/expo/pull/39716) by [@Wenszel](https://github.com/Wenszel))
|
|
@@ -18,8 +19,10 @@
|
|
|
18
19
|
|
|
19
20
|
### 💡 Others
|
|
20
21
|
|
|
22
|
+
- [next][android] Refactor permissions ([#40076](https://github.com/expo/expo/pull/40076) by [@Wenszel](https://github.com/Wenszel))
|
|
21
23
|
- [next] Add test screens ([#39951](https://github.com/expo/expo/pull/39951) by [@Wenszel](https://github.com/Wenszel))
|
|
22
24
|
- [next] Add documentation ([#39754](https://github.com/expo/expo/pull/39754) by [@Wenszel](https://github.com/Wenszel))
|
|
25
|
+
- Remove tests related files from the published package content. ([#39551](https://github.com/expo/expo/pull/39551) by [@Simek](https://github.com/Simek))
|
|
23
26
|
|
|
24
27
|
## 18.2.0 — 2025-09-16
|
|
25
28
|
|
package/android/build.gradle
CHANGED
|
@@ -4,13 +4,13 @@ plugins {
|
|
|
4
4
|
}
|
|
5
5
|
|
|
6
6
|
group = 'host.exp.exponent'
|
|
7
|
-
version = '18.3.0-canary-
|
|
7
|
+
version = '18.3.0-canary-20251031-b135dff'
|
|
8
8
|
|
|
9
9
|
android {
|
|
10
10
|
namespace "expo.modules.medialibrary"
|
|
11
11
|
defaultConfig {
|
|
12
12
|
versionCode 37
|
|
13
|
-
versionName "18.3.0-canary-
|
|
13
|
+
versionName "18.3.0-canary-20251031-b135dff"
|
|
14
14
|
}
|
|
15
15
|
}
|
|
16
16
|
|
|
@@ -47,7 +47,7 @@ class MediaLibraryNextModule : Module() {
|
|
|
47
47
|
|
|
48
48
|
private val albumFactory by lazy {
|
|
49
49
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
|
50
|
-
AlbumModernFactory(assetFactory, assetDeleter, context)
|
|
50
|
+
AlbumModernFactory(assetFactory, assetDeleter, mediaStorePermissionsDelegate, context)
|
|
51
51
|
} else {
|
|
52
52
|
AlbumLegacyFactory(assetFactory, assetDeleter, context)
|
|
53
53
|
}
|
|
@@ -55,9 +55,9 @@ class MediaLibraryNextModule : Module() {
|
|
|
55
55
|
|
|
56
56
|
private val assetFactory by lazy {
|
|
57
57
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
|
58
|
-
AssetModernFactory(assetDeleter, context)
|
|
58
|
+
AssetModernFactory(assetDeleter, mediaStorePermissionsDelegate, context)
|
|
59
59
|
} else {
|
|
60
|
-
AssetLegacyFactory(assetDeleter, context)
|
|
60
|
+
AssetLegacyFactory(assetDeleter, systemPermissionsDelegate, context)
|
|
61
61
|
}
|
|
62
62
|
}
|
|
63
63
|
|
|
@@ -65,7 +65,7 @@ class MediaLibraryNextModule : Module() {
|
|
|
65
65
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
|
66
66
|
AssetModernDeleter(mediaStorePermissionsDelegate)
|
|
67
67
|
} else {
|
|
68
|
-
AssetLegacyDeleter(context)
|
|
68
|
+
AssetLegacyDeleter(systemPermissionsDelegate, context)
|
|
69
69
|
}
|
|
70
70
|
}
|
|
71
71
|
|
|
@@ -78,62 +78,50 @@ class MediaLibraryNextModule : Module() {
|
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
Property("id") { self: Asset ->
|
|
81
|
-
systemPermissionsDelegate.requireSystemPermissions(false)
|
|
82
81
|
self.contentUri
|
|
83
82
|
}
|
|
84
83
|
|
|
85
84
|
AsyncFunction("getCreationTime") Coroutine { self: Asset ->
|
|
86
|
-
systemPermissionsDelegate.requireSystemPermissions(false)
|
|
87
85
|
self.getCreationTime()
|
|
88
86
|
}
|
|
89
87
|
|
|
90
88
|
AsyncFunction("getDuration") Coroutine { self: Asset ->
|
|
91
|
-
systemPermissionsDelegate.requireSystemPermissions(false)
|
|
92
89
|
self.getDuration()
|
|
93
90
|
}
|
|
94
91
|
|
|
95
92
|
AsyncFunction("getExif") Coroutine { self: Asset ->
|
|
96
|
-
systemPermissionsDelegate.requireSystemPermissions(false)
|
|
97
93
|
self.getExif()
|
|
98
94
|
}
|
|
99
95
|
|
|
100
96
|
AsyncFunction("getLocation") Coroutine { self: Asset ->
|
|
101
|
-
systemPermissionsDelegate.requireSystemPermissions(false)
|
|
102
97
|
self.getLocation()
|
|
103
98
|
}
|
|
104
99
|
|
|
105
100
|
AsyncFunction("getFilename") Coroutine { self: Asset ->
|
|
106
|
-
systemPermissionsDelegate.requireSystemPermissions(false)
|
|
107
101
|
self.getFilename()
|
|
108
102
|
}
|
|
109
103
|
|
|
110
104
|
AsyncFunction("getHeight") Coroutine { self: Asset ->
|
|
111
|
-
systemPermissionsDelegate.requireSystemPermissions(false)
|
|
112
105
|
self.getHeight()
|
|
113
106
|
}
|
|
114
107
|
|
|
115
108
|
AsyncFunction("getMediaType") Coroutine { self: Asset ->
|
|
116
|
-
systemPermissionsDelegate.requireSystemPermissions(false)
|
|
117
109
|
self.getMediaType()
|
|
118
110
|
}
|
|
119
111
|
|
|
120
112
|
AsyncFunction("getModificationTime") Coroutine { self: Asset ->
|
|
121
|
-
systemPermissionsDelegate.requireSystemPermissions(false)
|
|
122
113
|
self.getModificationTime()
|
|
123
114
|
}
|
|
124
115
|
|
|
125
116
|
AsyncFunction("getUri") Coroutine { self: Asset ->
|
|
126
|
-
systemPermissionsDelegate.requireSystemPermissions(false)
|
|
127
117
|
self.getUri()
|
|
128
118
|
}
|
|
129
119
|
|
|
130
120
|
AsyncFunction("getWidth") Coroutine { self: Asset ->
|
|
131
|
-
systemPermissionsDelegate.requireSystemPermissions(false)
|
|
132
121
|
self.getWidth()
|
|
133
122
|
}
|
|
134
123
|
|
|
135
124
|
AsyncFunction("delete") Coroutine { self: Asset ->
|
|
136
|
-
systemPermissionsDelegate.requireSystemPermissions(true)
|
|
137
125
|
self.delete()
|
|
138
126
|
}
|
|
139
127
|
}
|
|
@@ -148,23 +136,18 @@ class MediaLibraryNextModule : Module() {
|
|
|
148
136
|
}
|
|
149
137
|
|
|
150
138
|
AsyncFunction("getTitle") Coroutine { self: Album ->
|
|
151
|
-
systemPermissionsDelegate.requireSystemPermissions(false)
|
|
152
139
|
self.getTitle()
|
|
153
140
|
}
|
|
154
141
|
|
|
155
142
|
AsyncFunction("getAssets") Coroutine { self: Album ->
|
|
156
|
-
systemPermissionsDelegate.requireSystemPermissions(false)
|
|
157
143
|
self.getAssets()
|
|
158
144
|
}
|
|
159
145
|
|
|
160
146
|
AsyncFunction("add") Coroutine { self: Album, asset: Asset ->
|
|
161
|
-
systemPermissionsDelegate.requireSystemPermissions(true)
|
|
162
|
-
mediaStorePermissionsDelegate.requestMediaLibraryWritePermission(listOf(asset.contentUri))
|
|
163
147
|
self.add(asset)
|
|
164
148
|
}
|
|
165
149
|
|
|
166
150
|
AsyncFunction("delete") Coroutine { self: Album ->
|
|
167
|
-
systemPermissionsDelegate.requireSystemPermissions(true)
|
|
168
151
|
self.delete()
|
|
169
152
|
}
|
|
170
153
|
}
|
|
@@ -227,13 +210,11 @@ class MediaLibraryNextModule : Module() {
|
|
|
227
210
|
}
|
|
228
211
|
|
|
229
212
|
AsyncFunction("createAsset") Coroutine { filePath: Uri, album: Album? ->
|
|
230
|
-
systemPermissionsDelegate.requireSystemPermissions(true)
|
|
231
213
|
return@Coroutine assetFactory.create(filePath, album?.getRelativePath())
|
|
232
214
|
}
|
|
233
215
|
|
|
234
216
|
@OptIn(EitherType::class)
|
|
235
217
|
AsyncFunction("createAlbum") Coroutine { name: String, assetRefs: Either<List<Asset>, List<Uri>>, move: Boolean ->
|
|
236
|
-
systemPermissionsDelegate.requireSystemPermissions(true)
|
|
237
218
|
val assetListKClass = toKClass<List<Asset>>()
|
|
238
219
|
if (assetRefs.`is`(assetListKClass)) {
|
|
239
220
|
val assetList = assetRefs.get(assetListKClass)
|
|
@@ -244,12 +225,10 @@ class MediaLibraryNextModule : Module() {
|
|
|
244
225
|
}
|
|
245
226
|
|
|
246
227
|
AsyncFunction("getAlbum") Coroutine { title: String ->
|
|
247
|
-
systemPermissionsDelegate.requireSystemPermissions(false)
|
|
248
228
|
albumQuery.getAlbum(title)
|
|
249
229
|
}
|
|
250
230
|
|
|
251
231
|
AsyncFunction("deleteAlbums") Coroutine { albums: List<Album> ->
|
|
252
|
-
systemPermissionsDelegate.requireSystemPermissions(true)
|
|
253
232
|
val contentUris = albums
|
|
254
233
|
.map { it.getAssets() }
|
|
255
234
|
.flatten()
|
|
@@ -258,7 +237,6 @@ class MediaLibraryNextModule : Module() {
|
|
|
258
237
|
}
|
|
259
238
|
|
|
260
239
|
AsyncFunction("deleteAssets") Coroutine { assets: List<Asset> ->
|
|
261
|
-
systemPermissionsDelegate.requireSystemPermissions(true)
|
|
262
240
|
assetDeleter.delete(assets.map { it.contentUri })
|
|
263
241
|
}
|
|
264
242
|
|
package/android/src/main/java/expo/modules/medialibrary/next/exceptions/PermissionExceptions.kt
CHANGED
|
@@ -2,5 +2,4 @@ package expo.modules.medialibrary.next.exceptions
|
|
|
2
2
|
|
|
3
3
|
import expo.modules.kotlin.exception.CodedException
|
|
4
4
|
|
|
5
|
-
class PermissionException(message: String) :
|
|
6
|
-
CodedException(message)
|
|
5
|
+
class PermissionException(message: String, cause: Throwable? = null) : CodedException(message, cause)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
package expo.modules.medialibrary.next.extensions
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.media.MediaScannerConnection
|
|
5
|
+
import android.net.Uri
|
|
6
|
+
import kotlin.coroutines.resume
|
|
7
|
+
import kotlin.coroutines.suspendCoroutine
|
|
8
|
+
|
|
9
|
+
suspend fun Context.scanFile(
|
|
10
|
+
path: String,
|
|
11
|
+
mimeType: String? = null
|
|
12
|
+
): Pair<String, Uri?> = suspendCoroutine { continuation ->
|
|
13
|
+
MediaScannerConnection.scanFile(this, arrayOf(path), arrayOf(mimeType)) { path, uri ->
|
|
14
|
+
continuation.resume(Pair(path, uri))
|
|
15
|
+
}
|
|
16
|
+
}
|
package/android/src/main/java/expo/modules/medialibrary/next/extensions/resolver/QueryOne.kt
CHANGED
|
@@ -16,7 +16,7 @@ suspend fun <T> ContentResolver.queryOne(
|
|
|
16
16
|
sortOrder: String? = null
|
|
17
17
|
): T? = withContext(Dispatchers.IO) {
|
|
18
18
|
val projection = arrayOf(column)
|
|
19
|
-
|
|
19
|
+
safeQuery(contentUri, projection, selection, selectionArgs, sortOrder)?.use { cursor ->
|
|
20
20
|
ensureActive()
|
|
21
21
|
val index = cursor.getColumnIndexOrThrow(column)
|
|
22
22
|
return@withContext if (cursor.moveToFirst()) {
|
|
@@ -25,5 +25,4 @@ suspend fun <T> ContentResolver.queryOne(
|
|
|
25
25
|
null
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
|
-
return@withContext null
|
|
29
28
|
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
package expo.modules.medialibrary.next.extensions.resolver
|
|
2
|
+
|
|
3
|
+
import android.content.ContentResolver
|
|
4
|
+
import android.database.Cursor
|
|
5
|
+
import android.net.Uri
|
|
6
|
+
import expo.modules.medialibrary.next.exceptions.PermissionException
|
|
7
|
+
|
|
8
|
+
fun ContentResolver.safeQuery(
|
|
9
|
+
uri: Uri,
|
|
10
|
+
projection: Array<String>,
|
|
11
|
+
selection: String? = null,
|
|
12
|
+
selectionArgs: Array<String>? = null,
|
|
13
|
+
sortOrder: String? = null
|
|
14
|
+
): Cursor? {
|
|
15
|
+
return try {
|
|
16
|
+
query(uri, projection, selection, selectionArgs, sortOrder)
|
|
17
|
+
} catch (e: SecurityException) {
|
|
18
|
+
throw PermissionException("Missing required system permissions", e)
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -14,6 +14,7 @@ import expo.modules.medialibrary.next.objects.asset.Asset
|
|
|
14
14
|
import expo.modules.medialibrary.next.objects.asset.deleters.AssetDeleter
|
|
15
15
|
import expo.modules.medialibrary.next.objects.wrappers.MimeType
|
|
16
16
|
import expo.modules.medialibrary.next.objects.asset.factories.AssetFactory
|
|
17
|
+
import expo.modules.medialibrary.next.permissions.MediaStorePermissionsDelegate
|
|
17
18
|
import java.io.IOException
|
|
18
19
|
import java.lang.ref.WeakReference
|
|
19
20
|
|
|
@@ -21,6 +22,7 @@ import java.lang.ref.WeakReference
|
|
|
21
22
|
class AlbumModernFactory(
|
|
22
23
|
private val assetFactory: AssetFactory,
|
|
23
24
|
private val assetDeleter: AssetDeleter,
|
|
25
|
+
private val mediaStorePermissionsDelegate: MediaStorePermissionsDelegate,
|
|
24
26
|
context: Context
|
|
25
27
|
) : AlbumFactory {
|
|
26
28
|
private val contextRef = WeakReference(context)
|
|
@@ -61,6 +63,7 @@ class AlbumModernFactory(
|
|
|
61
63
|
|
|
62
64
|
private suspend fun processAssetsLocation(assets: List<Asset>, relativePath: RelativePath, deleteOriginalAssets: Boolean) {
|
|
63
65
|
if (deleteOriginalAssets) {
|
|
66
|
+
mediaStorePermissionsDelegate.requestMediaLibraryWritePermission(assets.map { it.contentUri })
|
|
64
67
|
assets.map { it.move(relativePath) }
|
|
65
68
|
} else {
|
|
66
69
|
assets.map { it.copy(relativePath) }
|
|
@@ -8,7 +8,6 @@ import android.os.Bundle
|
|
|
8
8
|
import androidx.annotation.DeprecatedSinceApi
|
|
9
9
|
import androidx.core.net.toUri
|
|
10
10
|
import androidx.exifinterface.media.ExifInterface
|
|
11
|
-
import expo.modules.medialibrary.MediaLibraryUtils
|
|
12
11
|
import expo.modules.medialibrary.next.exceptions.AssetCouldNotBeCreated
|
|
13
12
|
import expo.modules.medialibrary.next.exceptions.AssetPropertyNotFoundException
|
|
14
13
|
import expo.modules.medialibrary.next.exceptions.ContentResolverNotObtainedException
|
|
@@ -23,12 +22,14 @@ import expo.modules.medialibrary.next.extensions.resolver.queryAssetWidth
|
|
|
23
22
|
import expo.modules.medialibrary.next.extensions.resolver.queryAssetCreationTime
|
|
24
23
|
import expo.modules.medialibrary.next.extensions.safeCopy
|
|
25
24
|
import expo.modules.medialibrary.next.extensions.safeMove
|
|
25
|
+
import expo.modules.medialibrary.next.extensions.scanFile
|
|
26
26
|
import expo.modules.medialibrary.next.objects.wrappers.RelativePath
|
|
27
27
|
import expo.modules.medialibrary.next.objects.asset.Asset
|
|
28
28
|
import expo.modules.medialibrary.next.objects.asset.EXIF_TAGS
|
|
29
29
|
import expo.modules.medialibrary.next.objects.asset.deleters.AssetDeleter
|
|
30
30
|
import expo.modules.medialibrary.next.objects.wrappers.MediaType
|
|
31
31
|
import expo.modules.medialibrary.next.objects.wrappers.MimeType
|
|
32
|
+
import expo.modules.medialibrary.next.permissions.SystemPermissionsDelegate
|
|
32
33
|
import expo.modules.medialibrary.next.records.Location
|
|
33
34
|
import kotlinx.coroutines.Dispatchers
|
|
34
35
|
import kotlinx.coroutines.ensureActive
|
|
@@ -44,6 +45,7 @@ import kotlin.time.toDuration
|
|
|
44
45
|
class AssetLegacyDelegate(
|
|
45
46
|
contentUri: Uri,
|
|
46
47
|
val assetDeleter: AssetDeleter,
|
|
48
|
+
val systemPermissionsDelegate: SystemPermissionsDelegate,
|
|
47
49
|
context: Context
|
|
48
50
|
) : AssetDelegate {
|
|
49
51
|
private val contextRef = WeakReference(context)
|
|
@@ -121,14 +123,17 @@ class AssetLegacyDelegate(
|
|
|
121
123
|
?: MimeType.from(getUri())
|
|
122
124
|
}
|
|
123
125
|
|
|
124
|
-
override suspend fun getLocation(): Location?
|
|
125
|
-
|
|
126
|
+
override suspend fun getLocation(): Location? {
|
|
127
|
+
systemPermissionsDelegate.requireReadPermissions()
|
|
128
|
+
return contentResolver.openInputStream(contentUri)?.use { stream ->
|
|
126
129
|
ExifInterface(stream)
|
|
127
130
|
.latLong
|
|
128
131
|
?.let { (lat, long) -> Location(lat, long) }
|
|
129
132
|
}
|
|
133
|
+
}
|
|
130
134
|
|
|
131
135
|
override suspend fun getExif(): Bundle = withContext(Dispatchers.IO) {
|
|
136
|
+
systemPermissionsDelegate.requireReadPermissions()
|
|
132
137
|
if (getMediaType() != MediaType.IMAGE) {
|
|
133
138
|
return@withContext Bundle()
|
|
134
139
|
}
|
|
@@ -163,11 +168,12 @@ class AssetLegacyDelegate(
|
|
|
163
168
|
}
|
|
164
169
|
|
|
165
170
|
override suspend fun move(relativePath: RelativePath) = withContext(Dispatchers.IO) {
|
|
171
|
+
systemPermissionsDelegate.requireWritePermissions()
|
|
166
172
|
val path = contentResolver.queryAssetPath(contentUri)
|
|
167
173
|
?: throw AssetPropertyNotFoundException("Asset path")
|
|
168
174
|
val newFile = File(path).safeMove(File(relativePath.toFilePath()))
|
|
169
175
|
contentResolver.deleteBy(path)
|
|
170
|
-
val (_, uri) =
|
|
176
|
+
val (_, uri) = contextRef.getOrThrow().scanFile(newFile.path, null)
|
|
171
177
|
this@AssetLegacyDelegate.contentUri = uri
|
|
172
178
|
?: throw AssetCouldNotBeCreated("Could not create a new asset while moving the old one")
|
|
173
179
|
}
|
|
@@ -176,11 +182,11 @@ class AssetLegacyDelegate(
|
|
|
176
182
|
val path = contentResolver.queryAssetPath(contentUri)
|
|
177
183
|
?: throw AssetPropertyNotFoundException("Asset path")
|
|
178
184
|
val newFile = File(path).safeCopy(File(relativePath.toFilePath()))
|
|
179
|
-
val (_, uri) =
|
|
185
|
+
val (_, uri) = contextRef.getOrThrow().scanFile(newFile.path, null)
|
|
180
186
|
if (uri == null) {
|
|
181
187
|
throw AssetCouldNotBeCreated("Could not create a new asset while copying the old one")
|
|
182
188
|
}
|
|
183
|
-
val newAssetDelegate = AssetLegacyDelegate(contentUri, assetDeleter, contextRef.getOrThrow())
|
|
189
|
+
val newAssetDelegate = AssetLegacyDelegate(contentUri, assetDeleter, systemPermissionsDelegate, contextRef.getOrThrow())
|
|
184
190
|
return@withContext Asset(newAssetDelegate)
|
|
185
191
|
}
|
|
186
192
|
}
|
|
@@ -28,6 +28,7 @@ import expo.modules.medialibrary.next.objects.asset.EXIF_TAGS
|
|
|
28
28
|
import expo.modules.medialibrary.next.objects.asset.deleters.AssetDeleter
|
|
29
29
|
import expo.modules.medialibrary.next.objects.wrappers.MediaType
|
|
30
30
|
import expo.modules.medialibrary.next.objects.wrappers.MimeType
|
|
31
|
+
import expo.modules.medialibrary.next.permissions.MediaStorePermissionsDelegate
|
|
31
32
|
import expo.modules.medialibrary.next.records.Location
|
|
32
33
|
import kotlinx.coroutines.Dispatchers
|
|
33
34
|
import kotlinx.coroutines.ensureActive
|
|
@@ -44,6 +45,7 @@ import kotlin.time.toDuration
|
|
|
44
45
|
class AssetModernDelegate(
|
|
45
46
|
override val contentUri: Uri,
|
|
46
47
|
val assetDeleter: AssetDeleter,
|
|
48
|
+
val mediaStorePermissionsDelegate: MediaStorePermissionsDelegate,
|
|
47
49
|
context: Context
|
|
48
50
|
) : AssetDelegate {
|
|
49
51
|
private val contextRef = WeakReference(context)
|
|
@@ -75,21 +77,19 @@ class AssetModernDelegate(
|
|
|
75
77
|
|
|
76
78
|
override suspend fun getHeight(): Int {
|
|
77
79
|
val height = contentResolver.queryAssetHeight(contentUri)
|
|
78
|
-
?: throw AssetPropertyNotFoundException("Height")
|
|
79
80
|
// If height is not saved to the database
|
|
80
|
-
if (getMediaType() == MediaType.IMAGE && height <= 0) {
|
|
81
|
+
if (getMediaType() == MediaType.IMAGE && height != null && height <= 0) {
|
|
81
82
|
return downloadBitmapAndGet { it.outHeight }
|
|
82
83
|
}
|
|
83
|
-
return height
|
|
84
|
+
return height ?: throw AssetPropertyNotFoundException("Height")
|
|
84
85
|
}
|
|
85
86
|
|
|
86
87
|
override suspend fun getWidth(): Int {
|
|
87
88
|
val width = contentResolver.queryAssetWidth(contentUri)
|
|
88
|
-
|
|
89
|
-
if (getMediaType() == MediaType.IMAGE && width <= 0) {
|
|
89
|
+
if (getMediaType() == MediaType.IMAGE && width != null && width <= 0) {
|
|
90
90
|
return downloadBitmapAndGet { it.outWidth }
|
|
91
91
|
}
|
|
92
|
-
return width
|
|
92
|
+
return width ?: throw AssetPropertyNotFoundException("Width")
|
|
93
93
|
}
|
|
94
94
|
|
|
95
95
|
private suspend fun downloadBitmapAndGet(extract: (BitmapFactory.Options) -> Int): Int {
|
|
@@ -155,6 +155,7 @@ class AssetModernDelegate(
|
|
|
155
155
|
}
|
|
156
156
|
|
|
157
157
|
override suspend fun move(relativePath: RelativePath) {
|
|
158
|
+
mediaStorePermissionsDelegate.requestMediaLibraryWritePermission(listOf(contentUri))
|
|
158
159
|
contentResolver.updateRelativePath(contentUri, relativePath)
|
|
159
160
|
}
|
|
160
161
|
|
|
@@ -162,7 +163,7 @@ class AssetModernDelegate(
|
|
|
162
163
|
val newAssetUri = contentResolver.insertPendingAsset(getFilename(), getMimeType(), relativePath)
|
|
163
164
|
contentResolver.copyUriContent(contentUri, newAssetUri)
|
|
164
165
|
contentResolver.publishPendingAsset(newAssetUri)
|
|
165
|
-
val newAssetDelegate = AssetModernDelegate(newAssetUri, assetDeleter, contextRef.getOrThrow())
|
|
166
|
+
val newAssetDelegate = AssetModernDelegate(newAssetUri, assetDeleter, mediaStorePermissionsDelegate, contextRef.getOrThrow())
|
|
166
167
|
return@withContext Asset(newAssetDelegate)
|
|
167
168
|
}
|
|
168
169
|
}
|
|
@@ -9,6 +9,7 @@ import expo.modules.medialibrary.next.exceptions.AssetPropertyNotFoundException
|
|
|
9
9
|
import expo.modules.medialibrary.next.exceptions.ContentResolverNotObtainedException
|
|
10
10
|
import expo.modules.medialibrary.next.extensions.getOrThrow
|
|
11
11
|
import expo.modules.medialibrary.next.extensions.resolver.queryAssetPath
|
|
12
|
+
import expo.modules.medialibrary.next.permissions.SystemPermissionsDelegate
|
|
12
13
|
import kotlinx.coroutines.Dispatchers
|
|
13
14
|
import kotlinx.coroutines.async
|
|
14
15
|
import kotlinx.coroutines.awaitAll
|
|
@@ -16,8 +17,11 @@ import kotlinx.coroutines.withContext
|
|
|
16
17
|
import java.io.File
|
|
17
18
|
import java.lang.ref.WeakReference
|
|
18
19
|
|
|
19
|
-
@DeprecatedSinceApi(Build.VERSION_CODES.
|
|
20
|
-
class AssetLegacyDeleter(
|
|
20
|
+
@DeprecatedSinceApi(Build.VERSION_CODES.R)
|
|
21
|
+
class AssetLegacyDeleter(
|
|
22
|
+
val systemPermissionsDelegate: SystemPermissionsDelegate,
|
|
23
|
+
context: Context
|
|
24
|
+
) : AssetDeleter {
|
|
21
25
|
private val contextRef = WeakReference(context)
|
|
22
26
|
|
|
23
27
|
private val contentResolver
|
|
@@ -26,6 +30,7 @@ class AssetLegacyDeleter(context: Context) : AssetDeleter {
|
|
|
26
30
|
.contentResolver ?: throw ContentResolverNotObtainedException()
|
|
27
31
|
|
|
28
32
|
override suspend fun delete(contentUri: Uri): Unit = withContext(Dispatchers.IO) {
|
|
33
|
+
systemPermissionsDelegate.requireWritePermissions()
|
|
29
34
|
val path = contentResolver.queryAssetPath(contentUri)
|
|
30
35
|
?: throw AssetPropertyNotFoundException("Uri")
|
|
31
36
|
if (!File(path).delete()) {
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
package expo.modules.medialibrary.next.objects.asset.factories
|
|
2
2
|
|
|
3
3
|
import android.content.Context
|
|
4
|
+
import android.media.MediaScannerConnection
|
|
4
5
|
import android.net.Uri
|
|
5
6
|
import android.os.Build
|
|
6
7
|
import androidx.annotation.DeprecatedSinceApi
|
|
7
8
|
import androidx.core.net.toFile
|
|
8
|
-
import expo.modules.medialibrary.MediaLibraryUtils
|
|
9
9
|
import expo.modules.medialibrary.next.exceptions.AssetCouldNotBeCreated
|
|
10
10
|
import expo.modules.medialibrary.next.exceptions.ContentResolverNotObtainedException
|
|
11
11
|
import expo.modules.medialibrary.next.extensions.getOrThrow
|
|
@@ -16,15 +16,19 @@ import expo.modules.medialibrary.next.objects.asset.delegates.AssetDelegate
|
|
|
16
16
|
import expo.modules.medialibrary.next.objects.asset.delegates.AssetLegacyDelegate
|
|
17
17
|
import expo.modules.medialibrary.next.objects.asset.deleters.AssetDeleter
|
|
18
18
|
import expo.modules.medialibrary.next.objects.wrappers.MimeType
|
|
19
|
+
import expo.modules.medialibrary.next.permissions.SystemPermissionsDelegate
|
|
19
20
|
import kotlinx.coroutines.Dispatchers
|
|
20
21
|
import kotlinx.coroutines.ensureActive
|
|
21
22
|
import kotlinx.coroutines.withContext
|
|
22
23
|
import java.io.File
|
|
23
24
|
import java.lang.ref.WeakReference
|
|
25
|
+
import kotlin.coroutines.resume
|
|
26
|
+
import kotlin.coroutines.suspendCoroutine
|
|
24
27
|
|
|
25
28
|
@DeprecatedSinceApi(Build.VERSION_CODES.R)
|
|
26
29
|
class AssetLegacyFactory(
|
|
27
30
|
val assetDeleter: AssetDeleter,
|
|
31
|
+
val systemPermissionsDelegate: SystemPermissionsDelegate,
|
|
28
32
|
context: Context
|
|
29
33
|
) : AssetFactory {
|
|
30
34
|
private val contextRef = WeakReference(context)
|
|
@@ -35,7 +39,7 @@ class AssetLegacyFactory(
|
|
|
35
39
|
.contentResolver ?: throw ContentResolverNotObtainedException()
|
|
36
40
|
|
|
37
41
|
private fun createAssetDelegate(contentUri: Uri): AssetDelegate {
|
|
38
|
-
return AssetLegacyDelegate(contentUri, assetDeleter, contextRef.getOrThrow())
|
|
42
|
+
return AssetLegacyDelegate(contentUri, assetDeleter, systemPermissionsDelegate, contextRef.getOrThrow())
|
|
39
43
|
}
|
|
40
44
|
|
|
41
45
|
override fun create(contentUri: Uri): Asset {
|
|
@@ -44,24 +48,35 @@ class AssetLegacyFactory(
|
|
|
44
48
|
}
|
|
45
49
|
|
|
46
50
|
override suspend fun create(filePath: Uri, relativePath: RelativePath?): Asset = withContext(Dispatchers.IO) {
|
|
47
|
-
|
|
48
|
-
|
|
51
|
+
systemPermissionsDelegate.requireWritePermissions()
|
|
52
|
+
val destinationDirectory = createDestinationDirectory(filePath, relativePath)
|
|
53
|
+
val destinationFile = filePath
|
|
54
|
+
.toFile()
|
|
55
|
+
.safeCopy(destinationDirectory)
|
|
56
|
+
val (_, uri) = scanFile(contextRef.getOrThrow(), arrayOf(destinationFile.toString()), null)
|
|
57
|
+
ensureActive()
|
|
58
|
+
if (uri == null) {
|
|
59
|
+
throw AssetCouldNotBeCreated("Failed to create asset: could not add asset to MediaStore")
|
|
60
|
+
}
|
|
61
|
+
return@withContext create(uri)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
private fun createDestinationDirectory(filePath: Uri, relativePath: RelativePath?): File {
|
|
49
65
|
val destinationDirectory = if (relativePath != null) {
|
|
50
66
|
File(relativePath.toFilePath())
|
|
51
67
|
} else {
|
|
68
|
+
val mimeType = contentResolver.getType(filePath)?.let { MimeType(it) }
|
|
69
|
+
?: MimeType.from(filePath)
|
|
52
70
|
mimeType.externalStorageAssetDirectory()
|
|
53
71
|
}
|
|
54
72
|
destinationDirectory.mkdirs()
|
|
73
|
+
return destinationDirectory
|
|
74
|
+
}
|
|
55
75
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
.
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
if (uri == null) {
|
|
63
|
-
throw AssetCouldNotBeCreated("Failed to create asset: could not add asset to MediaStore")
|
|
76
|
+
private suspend fun scanFile(context: Context, paths: Array<String>, mimeTypes: Array<String>?) =
|
|
77
|
+
suspendCoroutine { complete ->
|
|
78
|
+
MediaScannerConnection.scanFile(context, paths, mimeTypes) { path: String, uri: Uri? ->
|
|
79
|
+
complete.resume(Pair(path, uri))
|
|
80
|
+
}
|
|
64
81
|
}
|
|
65
|
-
return@withContext create(uri)
|
|
66
|
-
}
|
|
67
82
|
}
|
|
@@ -15,6 +15,7 @@ import expo.modules.medialibrary.next.objects.asset.delegates.AssetDelegate
|
|
|
15
15
|
import expo.modules.medialibrary.next.objects.asset.delegates.AssetModernDelegate
|
|
16
16
|
import expo.modules.medialibrary.next.objects.asset.deleters.AssetDeleter
|
|
17
17
|
import expo.modules.medialibrary.next.objects.wrappers.MimeType
|
|
18
|
+
import expo.modules.medialibrary.next.permissions.MediaStorePermissionsDelegate
|
|
18
19
|
import kotlinx.coroutines.Dispatchers
|
|
19
20
|
import kotlinx.coroutines.ensureActive
|
|
20
21
|
import kotlinx.coroutines.withContext
|
|
@@ -23,6 +24,7 @@ import java.lang.ref.WeakReference
|
|
|
23
24
|
@RequiresApi(Build.VERSION_CODES.R)
|
|
24
25
|
class AssetModernFactory(
|
|
25
26
|
val assetDeleter: AssetDeleter,
|
|
27
|
+
val mediaStorePermissionsDelegate: MediaStorePermissionsDelegate,
|
|
26
28
|
context: Context
|
|
27
29
|
) : AssetFactory {
|
|
28
30
|
private val contextRef = WeakReference(context)
|
|
@@ -36,6 +38,7 @@ class AssetModernFactory(
|
|
|
36
38
|
return AssetModernDelegate(
|
|
37
39
|
contentUri,
|
|
38
40
|
assetDeleter,
|
|
41
|
+
mediaStorePermissionsDelegate,
|
|
39
42
|
contextRef.getOrThrow()
|
|
40
43
|
)
|
|
41
44
|
}
|
|
@@ -7,6 +7,7 @@ import android.provider.MediaStore
|
|
|
7
7
|
import androidx.annotation.DeprecatedSinceApi
|
|
8
8
|
import expo.modules.medialibrary.next.exceptions.QueryCouldNotBeExecuted
|
|
9
9
|
import expo.modules.medialibrary.next.extensions.resolver.EXTERNAL_CONTENT_URI
|
|
10
|
+
import expo.modules.medialibrary.next.extensions.resolver.safeQuery
|
|
10
11
|
import expo.modules.medialibrary.next.records.SortDescriptor
|
|
11
12
|
import kotlinx.coroutines.Dispatchers
|
|
12
13
|
import kotlinx.coroutines.withContext
|
|
@@ -26,7 +27,7 @@ class QueryLegacyExecutor(
|
|
|
26
27
|
val selection = buildSelection()
|
|
27
28
|
val sortOrder = buildSortOrder()
|
|
28
29
|
val selectionArgs = args.toTypedArray()
|
|
29
|
-
return@withContext contentResolver.
|
|
30
|
+
return@withContext contentResolver.safeQuery(EXTERNAL_CONTENT_URI, projection, selection, selectionArgs, sortOrder)
|
|
30
31
|
?: throw QueryCouldNotBeExecuted("Cursor is null")
|
|
31
32
|
}
|
|
32
33
|
|
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
package expo.modules.medialibrary.next.permissions
|
|
2
2
|
|
|
3
3
|
import android.content.Context
|
|
4
|
-
import android.content.Intent
|
|
5
|
-
import android.content.pm.PackageManager
|
|
6
4
|
import android.net.Uri
|
|
7
|
-
import android.os.Binder
|
|
8
5
|
import android.os.Build
|
|
9
6
|
import androidx.annotation.RequiresApi
|
|
10
7
|
import expo.modules.kotlin.AppContext
|
|
@@ -56,12 +53,9 @@ class MediaStorePermissionsDelegate(val appContext: AppContext) {
|
|
|
56
53
|
writeLauncher = registerForActivityResult(WriteContract(appContextProvider))
|
|
57
54
|
}
|
|
58
55
|
|
|
59
|
-
private fun hasWritePermissionForUri(uri: Uri): Boolean
|
|
60
|
-
|
|
61
|
-
uri,
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
Intent.FLAG_GRANT_WRITE_URI_PERMISSION
|
|
65
|
-
) == PackageManager.PERMISSION_GRANTED
|
|
66
|
-
}
|
|
56
|
+
private fun hasWritePermissionForUri(uri: Uri): Boolean =
|
|
57
|
+
runCatching {
|
|
58
|
+
context.contentResolver.openOutputStream(uri, "rw")?.close()
|
|
59
|
+
return true
|
|
60
|
+
}.getOrDefault(false)
|
|
67
61
|
}
|