expo-media-library 18.2.0 → 18.3.0-canary-20250930-9dc59d3

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.
Files changed (93) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/android/build.gradle +6 -6
  3. package/android/src/main/java/expo/modules/medialibrary/next/MediaLibraryNextModule.kt +48 -29
  4. package/android/src/main/java/expo/modules/medialibrary/next/extensions/resolver/AlbumExtensions.kt +9 -0
  5. package/android/src/main/java/expo/modules/medialibrary/next/extensions/resolver/AssetExtensions.kt +2 -5
  6. package/android/src/main/java/expo/modules/medialibrary/next/extensions/resolver/CursorExtensions.kt +15 -5
  7. package/android/src/main/java/expo/modules/medialibrary/next/objects/album/Album.kt +16 -17
  8. package/android/src/main/java/expo/modules/medialibrary/next/objects/album/AlbumQuery.kt +23 -0
  9. package/android/src/main/java/expo/modules/medialibrary/next/objects/album/factories/AlbumFactory.kt +1 -0
  10. package/android/src/main/java/expo/modules/medialibrary/next/objects/album/factories/AlbumLegacyFactory.kt +13 -4
  11. package/android/src/main/java/expo/modules/medialibrary/next/objects/album/factories/AlbumModernFactory.kt +13 -4
  12. package/android/src/main/java/expo/modules/medialibrary/next/objects/asset/Asset.kt +2 -18
  13. package/android/src/main/java/expo/modules/medialibrary/next/objects/asset/delegates/AssetLegacyDelegate.kt +17 -12
  14. package/android/src/main/java/expo/modules/medialibrary/next/objects/asset/delegates/AssetModernDelegate.kt +18 -7
  15. package/android/src/main/java/expo/modules/medialibrary/next/objects/asset/deleters/AssetDeleter.kt +8 -0
  16. package/android/src/main/java/expo/modules/medialibrary/next/objects/asset/deleters/AssetLegacyDeleter.kt +46 -0
  17. package/android/src/main/java/expo/modules/medialibrary/next/objects/asset/deleters/AssetModernDeleter.kt +21 -0
  18. package/android/src/main/java/expo/modules/medialibrary/next/objects/asset/factories/AssetFactory.kt +2 -1
  19. package/android/src/main/java/expo/modules/medialibrary/next/objects/asset/factories/AssetLegacyFactory.kt +26 -11
  20. package/android/src/main/java/expo/modules/medialibrary/next/objects/asset/factories/AssetModernFactory.kt +22 -3
  21. package/android/src/main/java/expo/modules/medialibrary/next/objects/query/MediaStoreQueryFormatter.kt +9 -3
  22. package/android/src/main/java/expo/modules/medialibrary/next/objects/query/Query.kt +6 -3
  23. package/android/src/main/java/expo/modules/medialibrary/next/objects/wrappers/MimeType.kt +10 -3
  24. package/android/src/main/java/expo/modules/medialibrary/next/objects/wrappers/RelativePath.kt +2 -1
  25. package/android/src/main/java/expo/modules/medialibrary/next/permissions/MediaStorePermissionsDelegate.kt +12 -6
  26. package/build/next/index.d.ts +10 -1
  27. package/build/next/index.d.ts.map +1 -1
  28. package/build/next/index.js +16 -2
  29. package/build/next/index.js.map +1 -1
  30. package/build/next/types/Album.d.ts +38 -17
  31. package/build/next/types/Album.d.ts.map +1 -1
  32. package/build/next/types/Album.js.map +1 -1
  33. package/build/next/types/Asset.d.ts +22 -26
  34. package/build/next/types/Asset.d.ts.map +1 -1
  35. package/build/next/types/Asset.js.map +1 -1
  36. package/build/next/types/Query.d.ts +1 -0
  37. package/build/next/types/Query.d.ts.map +1 -1
  38. package/build/next/types/Query.js.map +1 -1
  39. package/expo-module.config.json +1 -1
  40. package/ios/next/MediaLibraryNextModule.swift +17 -9
  41. package/ios/next/extensions/DateExtensions.swift +8 -0
  42. package/ios/next/objects/Query/PredicateBuilder.swift +1 -1
  43. package/ios/next/objects/Query/Query.swift +3 -3
  44. package/ios/next/objects/album/Album.swift +2 -2
  45. package/ios/next/objects/asset/Asset.swift +14 -7
  46. package/ios/next/repository/AssetCollectionRepository.swift +12 -0
  47. package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/{18.2.0/expo.modules.medialibrary-18.2.0-sources.jar → 18.3.0-canary-20250930-9dc59d3/expo.modules.medialibrary-18.3.0-canary-20250930-9dc59d3-sources.jar} +0 -0
  48. package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.3.0-canary-20250930-9dc59d3/expo.modules.medialibrary-18.3.0-canary-20250930-9dc59d3-sources.jar.md5 +1 -0
  49. package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.3.0-canary-20250930-9dc59d3/expo.modules.medialibrary-18.3.0-canary-20250930-9dc59d3-sources.jar.sha1 +1 -0
  50. package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.3.0-canary-20250930-9dc59d3/expo.modules.medialibrary-18.3.0-canary-20250930-9dc59d3-sources.jar.sha256 +1 -0
  51. package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.3.0-canary-20250930-9dc59d3/expo.modules.medialibrary-18.3.0-canary-20250930-9dc59d3-sources.jar.sha512 +1 -0
  52. package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.3.0-canary-20250930-9dc59d3/expo.modules.medialibrary-18.3.0-canary-20250930-9dc59d3.aar +0 -0
  53. package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.3.0-canary-20250930-9dc59d3/expo.modules.medialibrary-18.3.0-canary-20250930-9dc59d3.aar.md5 +1 -0
  54. package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.3.0-canary-20250930-9dc59d3/expo.modules.medialibrary-18.3.0-canary-20250930-9dc59d3.aar.sha1 +1 -0
  55. package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.3.0-canary-20250930-9dc59d3/expo.modules.medialibrary-18.3.0-canary-20250930-9dc59d3.aar.sha256 +1 -0
  56. package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.3.0-canary-20250930-9dc59d3/expo.modules.medialibrary-18.3.0-canary-20250930-9dc59d3.aar.sha512 +1 -0
  57. package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/{18.2.0/expo.modules.medialibrary-18.2.0.module → 18.3.0-canary-20250930-9dc59d3/expo.modules.medialibrary-18.3.0-canary-20250930-9dc59d3.module} +27 -27
  58. package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.3.0-canary-20250930-9dc59d3/expo.modules.medialibrary-18.3.0-canary-20250930-9dc59d3.module.md5 +1 -0
  59. package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.3.0-canary-20250930-9dc59d3/expo.modules.medialibrary-18.3.0-canary-20250930-9dc59d3.module.sha1 +1 -0
  60. package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.3.0-canary-20250930-9dc59d3/expo.modules.medialibrary-18.3.0-canary-20250930-9dc59d3.module.sha256 +1 -0
  61. package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.3.0-canary-20250930-9dc59d3/expo.modules.medialibrary-18.3.0-canary-20250930-9dc59d3.module.sha512 +1 -0
  62. package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/{18.2.0/expo.modules.medialibrary-18.2.0.pom → 18.3.0-canary-20250930-9dc59d3/expo.modules.medialibrary-18.3.0-canary-20250930-9dc59d3.pom} +4 -4
  63. package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.3.0-canary-20250930-9dc59d3/expo.modules.medialibrary-18.3.0-canary-20250930-9dc59d3.pom.md5 +1 -0
  64. package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.3.0-canary-20250930-9dc59d3/expo.modules.medialibrary-18.3.0-canary-20250930-9dc59d3.pom.sha1 +1 -0
  65. package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.3.0-canary-20250930-9dc59d3/expo.modules.medialibrary-18.3.0-canary-20250930-9dc59d3.pom.sha256 +1 -0
  66. package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.3.0-canary-20250930-9dc59d3/expo.modules.medialibrary-18.3.0-canary-20250930-9dc59d3.pom.sha512 +1 -0
  67. package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/maven-metadata.xml +4 -4
  68. package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/maven-metadata.xml.md5 +1 -1
  69. package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/maven-metadata.xml.sha1 +1 -1
  70. package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/maven-metadata.xml.sha256 +1 -1
  71. package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/maven-metadata.xml.sha512 +1 -1
  72. package/package.json +4 -5
  73. package/src/next/index.ts +19 -2
  74. package/src/next/types/Album.ts +39 -21
  75. package/src/next/types/Asset.ts +31 -20
  76. package/src/next/types/Query.ts +1 -0
  77. package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.2.0/expo.modules.medialibrary-18.2.0-sources.jar.md5 +0 -1
  78. package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.2.0/expo.modules.medialibrary-18.2.0-sources.jar.sha1 +0 -1
  79. package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.2.0/expo.modules.medialibrary-18.2.0-sources.jar.sha256 +0 -1
  80. package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.2.0/expo.modules.medialibrary-18.2.0-sources.jar.sha512 +0 -1
  81. package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.2.0/expo.modules.medialibrary-18.2.0.aar +0 -0
  82. package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.2.0/expo.modules.medialibrary-18.2.0.aar.md5 +0 -1
  83. package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.2.0/expo.modules.medialibrary-18.2.0.aar.sha1 +0 -1
  84. package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.2.0/expo.modules.medialibrary-18.2.0.aar.sha256 +0 -1
  85. package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.2.0/expo.modules.medialibrary-18.2.0.aar.sha512 +0 -1
  86. package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.2.0/expo.modules.medialibrary-18.2.0.module.md5 +0 -1
  87. package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.2.0/expo.modules.medialibrary-18.2.0.module.sha1 +0 -1
  88. package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.2.0/expo.modules.medialibrary-18.2.0.module.sha256 +0 -1
  89. package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.2.0/expo.modules.medialibrary-18.2.0.module.sha512 +0 -1
  90. package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.2.0/expo.modules.medialibrary-18.2.0.pom.md5 +0 -1
  91. package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.2.0/expo.modules.medialibrary-18.2.0.pom.sha1 +0 -1
  92. package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.2.0/expo.modules.medialibrary-18.2.0.pom.sha256 +0 -1
  93. package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.2.0/expo.modules.medialibrary-18.2.0.pom.sha512 +0 -1
package/CHANGELOG.md CHANGED
@@ -6,10 +6,20 @@
6
6
 
7
7
  ### 🎉 New features
8
8
 
9
+ - [next] Add `Album.get(title)` ([#39717](https://github.com/expo/expo/pull/39717) by [@Wenszel](https://github.com/Wenszel))
10
+
9
11
  ### 🐛 Bug fixes
10
12
 
13
+ - [next][iOS] Convert `id` to URI format ([#39920](https://github.com/expo/expo/pull/39920) by [@Wenszel](https://github.com/Wenszel))
14
+ - [next][android] Fix `delete()` throwing security exception ([#39914](https://github.com/expo/expo/pull/39914) by [@Wenszel](https://github.com/Wenszel))
15
+ - [next][android] Change default root directory to Pictures ([#39716](https://github.com/expo/expo/pull/39716) by [@Wenszel](https://github.com/Wenszel))
16
+ - [next] Fix `asset.getModificationTime` to return milliseconds ([#39715](https://github.com/expo/expo/pull/39715) by [@Wenszel](https://github.com/Wenszel))
17
+
11
18
  ### 💡 Others
12
19
 
20
+ - [next] Add test screens ([#39951](https://github.com/expo/expo/pull/39951) by [@Wenszel](https://github.com/Wenszel))
21
+ - [next] Add documentation ([#39754](https://github.com/expo/expo/pull/39754) by [@Wenszel](https://github.com/Wenszel))
22
+
13
23
  ## 18.2.0 — 2025-09-16
14
24
 
15
25
  ### 🎉 New features
@@ -4,23 +4,23 @@ plugins {
4
4
  }
5
5
 
6
6
  group = 'host.exp.exponent'
7
- version = '18.2.0'
7
+ version = '18.3.0-canary-20250930-9dc59d3'
8
8
 
9
9
  android {
10
10
  namespace "expo.modules.medialibrary"
11
11
  defaultConfig {
12
12
  versionCode 37
13
- versionName "18.2.0"
13
+ versionName "18.3.0-canary-20250930-9dc59d3"
14
14
  }
15
15
  }
16
16
 
17
17
  dependencies {
18
- implementation "androidx.annotation:annotation:1.2.0"
19
- api "androidx.exifinterface:exifinterface:1.3.3"
20
- implementation 'androidx.activity:activity-ktx:1.10.1'
18
+ implementation "androidx.annotation:annotation:1.9.1"
19
+ api "androidx.exifinterface:exifinterface:1.4.1"
20
+ implementation 'androidx.activity:activity-ktx:1.11.0'
21
21
 
22
22
  if (project.findProject(':expo-modules-test-core')) {
23
23
  testImplementation project(':expo-modules-test-core')
24
24
  }
25
- testImplementation "org.robolectric:robolectric:4.10"
25
+ testImplementation "org.robolectric:robolectric:4.16"
26
26
  }
@@ -11,9 +11,12 @@ import expo.modules.kotlin.modules.ModuleDefinition
11
11
  import expo.modules.kotlin.types.Either
12
12
  import expo.modules.kotlin.types.toKClass
13
13
  import expo.modules.medialibrary.next.objects.album.Album
14
+ import expo.modules.medialibrary.next.objects.album.AlbumQuery
14
15
  import expo.modules.medialibrary.next.objects.asset.Asset
15
16
  import expo.modules.medialibrary.next.objects.album.factories.AlbumModernFactory
16
17
  import expo.modules.medialibrary.next.objects.album.factories.AlbumLegacyFactory
18
+ import expo.modules.medialibrary.next.objects.asset.deleters.AssetLegacyDeleter
19
+ import expo.modules.medialibrary.next.objects.asset.deleters.AssetModernDeleter
17
20
  import expo.modules.medialibrary.next.objects.asset.factories.AssetModernFactory
18
21
  import expo.modules.medialibrary.next.objects.asset.factories.AssetLegacyFactory
19
22
  import expo.modules.medialibrary.next.objects.query.MediaStoreQueryFormatter
@@ -38,19 +41,31 @@ class MediaLibraryNextModule : Module() {
38
41
  MediaStorePermissionsDelegate(appContext)
39
42
  }
40
43
 
44
+ private val albumQuery by lazy {
45
+ AlbumQuery(albumFactory, context)
46
+ }
47
+
41
48
  private val albumFactory by lazy {
42
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
43
- AlbumModernFactory(assetFactory, context)
49
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
50
+ AlbumModernFactory(assetFactory, assetDeleter, context)
44
51
  } else {
45
- AlbumLegacyFactory(assetFactory, context)
52
+ AlbumLegacyFactory(assetFactory, assetDeleter, context)
46
53
  }
47
54
  }
48
55
 
49
56
  private val assetFactory by lazy {
50
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
51
- AssetModernFactory(context)
57
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
58
+ AssetModernFactory(assetDeleter, context)
59
+ } else {
60
+ AssetLegacyFactory(assetDeleter, context)
61
+ }
62
+ }
63
+
64
+ private val assetDeleter by lazy {
65
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
66
+ AssetModernDeleter(mediaStorePermissionsDelegate)
52
67
  } else {
53
- AssetLegacyFactory(context)
68
+ AssetLegacyDeleter(context)
54
69
  }
55
70
  }
56
71
 
@@ -59,7 +74,7 @@ class MediaLibraryNextModule : Module() {
59
74
 
60
75
  Class(Asset::class) {
61
76
  Constructor { contentUri: Uri ->
62
- Asset(contentUri, context)
77
+ assetFactory.create(contentUri)
63
78
  }
64
79
 
65
80
  Property("id") { self: Asset ->
@@ -109,14 +124,13 @@ class MediaLibraryNextModule : Module() {
109
124
 
110
125
  AsyncFunction("delete") Coroutine { self: Asset ->
111
126
  systemPermissionsDelegate.requireSystemPermissions(true)
112
- mediaStorePermissionsDelegate.requestMediaLibraryActionPermission(listOf(self.contentUri), needsDeletePermission = true)
113
127
  self.delete()
114
128
  }
115
129
  }
116
130
 
117
131
  Class(Album::class) {
118
132
  Constructor { id: String ->
119
- Album(id, context)
133
+ Album(id, assetDeleter, assetFactory, context)
120
134
  }
121
135
 
122
136
  Property("id") { self: Album ->
@@ -135,21 +149,19 @@ class MediaLibraryNextModule : Module() {
135
149
 
136
150
  AsyncFunction("add") Coroutine { self: Album, asset: Asset ->
137
151
  systemPermissionsDelegate.requireSystemPermissions(true)
138
- mediaStorePermissionsDelegate.requestMediaLibraryActionPermission(listOf(asset.contentUri))
152
+ mediaStorePermissionsDelegate.requestMediaLibraryWritePermission(listOf(asset.contentUri))
139
153
  self.add(asset)
140
154
  }
141
155
 
142
156
  AsyncFunction("delete") Coroutine { self: Album ->
143
157
  systemPermissionsDelegate.requireSystemPermissions(true)
144
- val assetIdsToDelete = self.getAssets().map { it.contentUri }
145
- mediaStorePermissionsDelegate.requestMediaLibraryActionPermission(assetIdsToDelete, needsDeletePermission = true)
146
158
  self.delete()
147
159
  }
148
160
  }
149
161
 
150
162
  Class(Query::class) {
151
163
  Constructor {
152
- Query(context)
164
+ Query(assetFactory, context)
153
165
  }
154
166
 
155
167
  Function("limit") { self: Query, limit: Int ->
@@ -164,29 +176,29 @@ class MediaLibraryNextModule : Module() {
164
176
  self.album(album)
165
177
  }
166
178
 
167
- Function("eq") { self: Query, field: AssetField, value: Either<MediaType, Int> ->
168
- self.eq(field, MediaStoreQueryFormatter.parse(value))
179
+ Function("eq") { self: Query, field: AssetField, value: Either<MediaType, Long> ->
180
+ self.eq(field, MediaStoreQueryFormatter.parse(field, value))
169
181
  }
170
182
 
171
- Function("within") { self: Query, field: AssetField, values: List<Either<MediaType, Int>> ->
172
- val stringValues = values.map { value -> MediaStoreQueryFormatter.parse(value) }
183
+ Function("within") { self: Query, field: AssetField, values: List<Either<MediaType, Long>> ->
184
+ val stringValues = values.map { value -> MediaStoreQueryFormatter.parse(field, value) }
173
185
  self.within(field, stringValues)
174
186
  }
175
187
 
176
- Function("gt") { self: Query, field: AssetField, value: Int ->
177
- self.gt(field, MediaStoreQueryFormatter.parse(value))
188
+ Function("gt") { self: Query, field: AssetField, value: Long ->
189
+ self.gt(field, MediaStoreQueryFormatter.parse(field, value))
178
190
  }
179
191
 
180
- Function("gte") { self: Query, field: AssetField, value: Int ->
181
- self.gte(field, MediaStoreQueryFormatter.parse(value))
192
+ Function("gte") { self: Query, field: AssetField, value: Long ->
193
+ self.gte(field, MediaStoreQueryFormatter.parse(field, value))
182
194
  }
183
195
 
184
- Function("lt") { self: Query, field: AssetField, value: Int ->
185
- self.lt(field, MediaStoreQueryFormatter.parse(value))
196
+ Function("lt") { self: Query, field: AssetField, value: Long ->
197
+ self.lt(field, MediaStoreQueryFormatter.parse(field, value))
186
198
  }
187
199
 
188
- Function("lte") { self: Query, field: AssetField, value: Int ->
189
- self.lte(field, MediaStoreQueryFormatter.parse(value))
200
+ Function("lte") { self: Query, field: AssetField, value: Long ->
201
+ self.lte(field, MediaStoreQueryFormatter.parse(field, value))
190
202
  }
191
203
 
192
204
  Function("orderBy") { self: Query, sortDescriptorRef: Either<AssetField, SortDescriptor> ->
@@ -221,16 +233,23 @@ class MediaLibraryNextModule : Module() {
221
233
  return@Coroutine albumFactory.createFromFilePaths(name, assetPaths)
222
234
  }
223
235
 
236
+ AsyncFunction("getAlbum") Coroutine { title: String ->
237
+ systemPermissionsDelegate.requireSystemPermissions(false)
238
+ albumQuery.getAlbum(title)
239
+ }
240
+
224
241
  AsyncFunction("deleteAlbums") Coroutine { albums: List<Album> ->
225
242
  systemPermissionsDelegate.requireSystemPermissions(true)
226
- albums.forEach { album -> album.delete() }
243
+ val contentUris = albums
244
+ .map { it.getAssets() }
245
+ .flatten()
246
+ .map { it.contentUri }
247
+ assetDeleter.delete(contentUris)
227
248
  }
228
249
 
229
250
  AsyncFunction("deleteAssets") Coroutine { assets: List<Asset> ->
230
251
  systemPermissionsDelegate.requireSystemPermissions(true)
231
- val assetIdsToDelete = assets.map { it.contentUri }
232
- mediaStorePermissionsDelegate.requestMediaLibraryActionPermission(assetIdsToDelete, needsDeletePermission = true)
233
- assets.forEach { asset -> asset.delete() }
252
+ assetDeleter.delete(assets.map { it.contentUri })
234
253
  }
235
254
 
236
255
  AsyncFunction("requestPermissionsAsync") { writeOnly: Boolean, permissions: List<GranularPermission>?, promise: Promise ->
@@ -48,6 +48,15 @@ suspend fun ContentResolver.queryAlbumId(relativePath: RelativePath): String? =
48
48
  arrayOf(relativePath.value)
49
49
  )
50
50
 
51
+ suspend fun ContentResolver.queryAlbumId(name: String): String? =
52
+ queryOne(
53
+ EXTERNAL_CONTENT_URI,
54
+ MediaStore.Files.FileColumns.BUCKET_ID,
55
+ Cursor::getString,
56
+ "${MediaStore.MediaColumns.BUCKET_DISPLAY_NAME} = ?",
57
+ arrayOf(name)
58
+ )
59
+
51
60
  suspend fun ContentResolver.queryAlbumAssetsContentUris(bucketId: String): List<Uri> =
52
61
  withContext(Dispatchers.IO) {
53
62
  val projection = arrayOf(
@@ -17,8 +17,8 @@ import kotlinx.coroutines.withContext
17
17
  suspend fun ContentResolver.queryAssetDisplayName(contentUri: Uri): String? =
18
18
  queryOne(contentUri, MediaStore.MediaColumns.DISPLAY_NAME, Cursor::getString)
19
19
 
20
- suspend fun ContentResolver.queryGetCreationTime(contentUri: Uri): Long? =
21
- queryOne(contentUri, MediaStore.MediaColumns.DATE_TAKEN, Cursor::getLong)
20
+ suspend fun ContentResolver.queryAssetCreationTime(contentUri: Uri): Long? =
21
+ queryOne(contentUri, MediaStore.Images.Media.DATE_TAKEN, Cursor::getLong)
22
22
 
23
23
  suspend fun ContentResolver.queryAssetModificationTime(contentUri: Uri): Long? =
24
24
  queryOne(contentUri, MediaStore.MediaColumns.DATE_MODIFIED, Cursor::getLong)
@@ -38,9 +38,6 @@ suspend fun ContentResolver.queryAssetPath(contentUri: Uri): String? =
38
38
  suspend fun ContentResolver.queryAssetBucketId(contentUri: Uri): Int? =
39
39
  queryOne(contentUri, MediaStore.MediaColumns.BUCKET_ID, Cursor::getInt)
40
40
 
41
- suspend fun ContentResolver.queryAssetMediaType(contentUri: Uri): Int? =
42
- queryOne(contentUri, MediaStore.Files.FileColumns.MEDIA_TYPE, Cursor::getInt)
43
-
44
41
  suspend fun ContentResolver.insertPendingAsset(
45
42
  displayName: String,
46
43
  mimeType: MimeType,
@@ -3,16 +3,26 @@ package expo.modules.medialibrary.next.extensions.resolver
3
3
  import android.content.ContentUris
4
4
  import android.database.Cursor
5
5
  import android.net.Uri
6
+ import android.os.Build
6
7
  import android.provider.MediaStore
7
8
 
8
9
  fun Cursor.extractAssetContentUri(idColumn: Int, typeColumn: Int): Uri {
9
10
  val id = getLong(idColumn)
10
11
  val mediaType = getInt(typeColumn)
11
- val baseUri = when (mediaType) {
12
- MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE -> MediaStore.Images.Media.EXTERNAL_CONTENT_URI
13
- MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO -> MediaStore.Video.Media.EXTERNAL_CONTENT_URI
14
- MediaStore.Files.FileColumns.MEDIA_TYPE_AUDIO -> MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
15
- else -> EXTERNAL_CONTENT_URI
12
+ val baseUri = if (Build.VERSION.SDK_INT == Build.VERSION_CODES.Q) {
13
+ when (mediaType) {
14
+ MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE -> MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
15
+ MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO -> MediaStore.Video.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
16
+ MediaStore.Files.FileColumns.MEDIA_TYPE_AUDIO -> MediaStore.Audio.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
17
+ else -> MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
18
+ }
19
+ } else {
20
+ when (mediaType) {
21
+ MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE -> MediaStore.Images.Media.EXTERNAL_CONTENT_URI
22
+ MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO -> MediaStore.Video.Media.EXTERNAL_CONTENT_URI
23
+ MediaStore.Files.FileColumns.MEDIA_TYPE_AUDIO -> MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
24
+ else -> EXTERNAL_CONTENT_URI
25
+ }
16
26
  }
17
27
  return ContentUris.withAppendedId(baseUri, id)
18
28
  }
@@ -12,14 +12,18 @@ import expo.modules.medialibrary.next.extensions.resolver.queryAlbumFilepath
12
12
  import expo.modules.medialibrary.next.extensions.resolver.queryAlbumRelativePath
13
13
  import expo.modules.medialibrary.next.extensions.resolver.queryAlbumTitle
14
14
  import expo.modules.medialibrary.next.objects.asset.Asset
15
+ import expo.modules.medialibrary.next.objects.asset.deleters.AssetDeleter
16
+ import expo.modules.medialibrary.next.objects.asset.factories.AssetFactory
15
17
  import expo.modules.medialibrary.next.objects.wrappers.RelativePath
16
- import kotlinx.coroutines.async
17
- import kotlinx.coroutines.awaitAll
18
- import kotlinx.coroutines.coroutineScope
19
18
  import java.io.File
20
19
  import java.lang.ref.WeakReference
21
20
 
22
- class Album(val id: String, context: Context) : SharedObject() {
21
+ class Album(
22
+ val id: String,
23
+ val assetDeleter: AssetDeleter,
24
+ val assetFactory: AssetFactory,
25
+ context: Context
26
+ ) : SharedObject() {
23
27
  private val contextRef = WeakReference(context)
24
28
 
25
29
  private val contentResolver
@@ -51,21 +55,16 @@ class Album(val id: String, context: Context) : SharedObject() {
51
55
  return RelativePath(relative)
52
56
  }
53
57
 
54
- suspend fun getAssets(): List<Asset> {
55
- return contentResolver
58
+ suspend fun getAssets(): List<Asset> =
59
+ contentResolver
56
60
  .queryAlbumAssetsContentUris(id)
57
- .map { contentUri -> Asset(contentUri, contextRef.getOrThrow()) }
58
- }
61
+ .map { assetFactory.create(it) }
59
62
 
60
- suspend fun delete() = coroutineScope {
61
- getAssets().map { asset ->
62
- async {
63
- asset.delete()
64
- }
65
- }.awaitAll()
66
- }
63
+ suspend fun delete() =
64
+ assetDeleter.delete(
65
+ getAssets().map { it.contentUri }
66
+ )
67
67
 
68
- suspend fun add(asset: Asset) {
68
+ suspend fun add(asset: Asset) =
69
69
  asset.move(getRelativePath())
70
- }
71
70
  }
@@ -0,0 +1,23 @@
1
+ package expo.modules.medialibrary.next.objects.album
2
+
3
+ import android.content.Context
4
+ import expo.modules.medialibrary.next.exceptions.ContentResolverNotObtainedException
5
+ import expo.modules.medialibrary.next.extensions.getOrThrow
6
+ import expo.modules.medialibrary.next.extensions.resolver.queryAlbumId
7
+ import expo.modules.medialibrary.next.objects.album.factories.AlbumFactory
8
+ import java.lang.ref.WeakReference
9
+
10
+ class AlbumQuery(val albumFactory: AlbumFactory, context: Context) {
11
+ private val contextRef = WeakReference(context)
12
+
13
+ private val contentResolver
14
+ get() = contextRef
15
+ .getOrThrow()
16
+ .contentResolver ?: throw ContentResolverNotObtainedException()
17
+
18
+ suspend fun getAlbum(title: String): Album? {
19
+ val id = contentResolver.queryAlbumId(title)
20
+ ?: return null
21
+ return albumFactory.create(id)
22
+ }
23
+ }
@@ -5,6 +5,7 @@ import expo.modules.medialibrary.next.objects.album.Album
5
5
  import expo.modules.medialibrary.next.objects.asset.Asset
6
6
 
7
7
  interface AlbumFactory {
8
+ fun create(id: String): Album
8
9
  suspend fun createFromAssets(albumName: String, assets: List<Asset>, deleteOriginalAssets: Boolean): Album
9
10
  suspend fun createFromFilePaths(albumName: String, filePaths: List<Uri>): Album
10
11
  }
@@ -12,14 +12,19 @@ import expo.modules.medialibrary.next.extensions.resolver.queryAssetBucketId
12
12
  import expo.modules.medialibrary.next.objects.album.Album
13
13
  import expo.modules.medialibrary.next.objects.wrappers.RelativePath
14
14
  import expo.modules.medialibrary.next.objects.asset.Asset
15
+ import expo.modules.medialibrary.next.objects.asset.deleters.AssetDeleter
15
16
  import expo.modules.medialibrary.next.objects.wrappers.MimeType
16
17
  import expo.modules.medialibrary.next.objects.asset.factories.AssetFactory
17
18
  import java.io.File
18
19
  import java.io.IOException
19
20
  import java.lang.ref.WeakReference
20
21
 
21
- @DeprecatedSinceApi(Build.VERSION_CODES.Q)
22
- class AlbumLegacyFactory(private val assetFactory: AssetFactory, context: Context) : AlbumFactory {
22
+ @DeprecatedSinceApi(Build.VERSION_CODES.R)
23
+ class AlbumLegacyFactory(
24
+ private val assetFactory: AssetFactory,
25
+ private val assetDeleter: AssetDeleter,
26
+ context: Context
27
+ ) : AlbumFactory {
23
28
  private val contextRef = WeakReference(context)
24
29
 
25
30
  private val contentResolver
@@ -27,6 +32,10 @@ class AlbumLegacyFactory(private val assetFactory: AssetFactory, context: Contex
27
32
  .getOrThrow()
28
33
  .contentResolver ?: throw AlbumCouldNotBeCreated("Failed to create album: ContentResolver is unavailable.")
29
34
 
35
+ override fun create(id: String): Album {
36
+ return Album(id, assetDeleter, assetFactory, contextRef.getOrThrow())
37
+ }
38
+
30
39
  override suspend fun createFromAssets(albumName: String, assets: List<Asset>, deleteOriginalAssets: Boolean): Album {
31
40
  try {
32
41
  val firstAsset = assets.firstOrNull()
@@ -37,7 +46,7 @@ class AlbumLegacyFactory(private val assetFactory: AssetFactory, context: Contex
37
46
  processAssetsLocation(assets, relativePath, true)
38
47
  val albumId = contentResolver.queryAssetBucketId(assets[0].contentUri)
39
48
  ?: throw AlbumNotFoundException("Could not find album with filePath: ${relativePath.toFilePath()}")
40
- return Album(albumId.toString(), contextRef.getOrThrow())
49
+ return Album(albumId.toString(), assetDeleter, assetFactory, contextRef.getOrThrow())
41
50
  } catch (e: SecurityException) {
42
51
  throw AlbumCouldNotBeCreated("Missing WRITE_EXTERNAL_STORAGE permission: ${e.message}", e)
43
52
  } catch (e: IOException) {
@@ -55,7 +64,7 @@ class AlbumLegacyFactory(private val assetFactory: AssetFactory, context: Contex
55
64
  }
56
65
  val albumId = contentResolver.queryAssetBucketId(assets[0].contentUri)
57
66
  ?: throw AlbumCouldNotBeCreated("Could not find album with relativePath: $relativePath")
58
- return Album(albumId.toString(), contextRef.getOrThrow())
67
+ return Album(albumId.toString(), assetDeleter, assetFactory, contextRef.getOrThrow())
59
68
  }
60
69
 
61
70
  private suspend fun processAssetsLocation(assets: List<Asset>, relativePath: RelativePath, deleteOriginalAssets: Boolean) {
@@ -11,13 +11,18 @@ import expo.modules.medialibrary.next.extensions.resolver.queryAlbumId
11
11
  import expo.modules.medialibrary.next.objects.album.Album
12
12
  import expo.modules.medialibrary.next.objects.wrappers.RelativePath
13
13
  import expo.modules.medialibrary.next.objects.asset.Asset
14
+ import expo.modules.medialibrary.next.objects.asset.deleters.AssetDeleter
14
15
  import expo.modules.medialibrary.next.objects.wrappers.MimeType
15
16
  import expo.modules.medialibrary.next.objects.asset.factories.AssetFactory
16
17
  import java.io.IOException
17
18
  import java.lang.ref.WeakReference
18
19
 
19
- @RequiresApi(Build.VERSION_CODES.Q)
20
- class AlbumModernFactory(private val assetFactory: AssetFactory, context: Context) : AlbumFactory {
20
+ @RequiresApi(Build.VERSION_CODES.R)
21
+ class AlbumModernFactory(
22
+ private val assetFactory: AssetFactory,
23
+ private val assetDeleter: AssetDeleter,
24
+ context: Context
25
+ ) : AlbumFactory {
21
26
  private val contextRef = WeakReference(context)
22
27
 
23
28
  private val contentResolver
@@ -25,6 +30,10 @@ class AlbumModernFactory(private val assetFactory: AssetFactory, context: Contex
25
30
  .getOrThrow()
26
31
  .contentResolver ?: throw AlbumCouldNotBeCreated("Failed to create album: ContentResolver is unavailable.")
27
32
 
33
+ override fun create(id: String): Album {
34
+ return Album(id, assetDeleter, assetFactory, contextRef.getOrThrow())
35
+ }
36
+
28
37
  override suspend fun createFromAssets(albumName: String, assets: List<Asset>, deleteOriginalAssets: Boolean): Album =
29
38
  try {
30
39
  val mimeTypeOfFirstAsset = assets[0].getMimeType()
@@ -32,7 +41,7 @@ class AlbumModernFactory(private val assetFactory: AssetFactory, context: Contex
32
41
  processAssetsLocation(assets, albumRelativePath, deleteOriginalAssets)
33
42
  val albumId = contentResolver.queryAlbumId(albumRelativePath)
34
43
  ?: throw AlbumNotFoundException("Could not find album with relativePath: $albumRelativePath")
35
- Album(albumId, contextRef.getOrThrow())
44
+ Album(albumId, assetDeleter, assetFactory, contextRef.getOrThrow())
36
45
  } catch (e: SecurityException) {
37
46
  throw AlbumCouldNotBeCreated("Security Exception: ${e.message}", e)
38
47
  } catch (e: IOException) {
@@ -47,7 +56,7 @@ class AlbumModernFactory(private val assetFactory: AssetFactory, context: Contex
47
56
  }
48
57
  val albumId = contentResolver.queryAlbumId(relativePath)
49
58
  ?: throw AlbumCouldNotBeCreated("Failed to create album: newly created album was not found in the MediaStore.")
50
- return Album(albumId, contextRef.getOrThrow())
59
+ return Album(albumId, assetDeleter, assetFactory, contextRef.getOrThrow())
51
60
  }
52
61
 
53
62
  private suspend fun processAssetsLocation(assets: List<Asset>, relativePath: RelativePath, deleteOriginalAssets: Boolean) {
@@ -1,31 +1,15 @@
1
1
  package expo.modules.medialibrary.next.objects.asset
2
2
 
3
- import android.content.Context
4
3
  import android.net.Uri
5
- import android.os.Build
6
4
  import expo.modules.kotlin.sharedobjects.SharedObject
7
- import expo.modules.medialibrary.next.extensions.getOrThrow
5
+ import expo.modules.medialibrary.next.objects.asset.delegates.AssetDelegate
8
6
  import expo.modules.medialibrary.next.objects.wrappers.RelativePath
9
- import expo.modules.medialibrary.next.objects.asset.delegates.AssetLegacyDelegate
10
- import expo.modules.medialibrary.next.objects.asset.delegates.AssetModernDelegate
11
7
  import expo.modules.medialibrary.next.objects.wrappers.MediaType
12
8
  import expo.modules.medialibrary.next.objects.wrappers.MimeType
13
9
  import kotlinx.coroutines.Dispatchers
14
10
  import kotlinx.coroutines.withContext
15
- import java.lang.ref.WeakReference
16
- import kotlin.getValue
17
-
18
- class Asset(contentUri: Uri, context: Context) : SharedObject() {
19
- private val contextRef = WeakReference(context)
20
-
21
- val assetDelegate by lazy {
22
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
23
- AssetModernDelegate(contentUri, contextRef.getOrThrow())
24
- } else {
25
- AssetLegacyDelegate(contentUri, contextRef.getOrThrow())
26
- }
27
- }
28
11
 
12
+ class Asset(val assetDelegate: AssetDelegate) : SharedObject() {
29
13
  val contentUri: Uri get() = assetDelegate.contentUri
30
14
 
31
15
  suspend fun getCreationTime(): Long? =
@@ -6,7 +6,6 @@ import android.net.Uri
6
6
  import android.os.Build
7
7
  import androidx.annotation.DeprecatedSinceApi
8
8
  import androidx.core.net.toUri
9
- import expo.modules.medialibrary.AssetFileException
10
9
  import expo.modules.medialibrary.MediaLibraryUtils
11
10
  import expo.modules.medialibrary.next.exceptions.AssetCouldNotBeCreated
12
11
  import expo.modules.medialibrary.next.exceptions.AssetPropertyNotFoundException
@@ -19,20 +18,27 @@ import expo.modules.medialibrary.next.extensions.resolver.queryAssetHeight
19
18
  import expo.modules.medialibrary.next.extensions.resolver.queryAssetModificationTime
20
19
  import expo.modules.medialibrary.next.extensions.resolver.queryAssetPath
21
20
  import expo.modules.medialibrary.next.extensions.resolver.queryAssetWidth
22
- import expo.modules.medialibrary.next.extensions.resolver.queryGetCreationTime
21
+ import expo.modules.medialibrary.next.extensions.resolver.queryAssetCreationTime
23
22
  import expo.modules.medialibrary.next.extensions.safeCopy
24
23
  import expo.modules.medialibrary.next.extensions.safeMove
25
24
  import expo.modules.medialibrary.next.objects.wrappers.RelativePath
26
25
  import expo.modules.medialibrary.next.objects.asset.Asset
26
+ import expo.modules.medialibrary.next.objects.asset.deleters.AssetDeleter
27
27
  import expo.modules.medialibrary.next.objects.wrappers.MediaType
28
28
  import expo.modules.medialibrary.next.objects.wrappers.MimeType
29
29
  import kotlinx.coroutines.Dispatchers
30
30
  import kotlinx.coroutines.withContext
31
31
  import java.io.File
32
32
  import java.lang.ref.WeakReference
33
+ import kotlin.time.DurationUnit
34
+ import kotlin.time.toDuration
33
35
 
34
36
  @DeprecatedSinceApi(Build.VERSION_CODES.Q)
35
- class AssetLegacyDelegate(contentUri: Uri, context: Context) : AssetDelegate {
37
+ class AssetLegacyDelegate(
38
+ contentUri: Uri,
39
+ val assetDeleter: AssetDeleter,
40
+ context: Context
41
+ ) : AssetDelegate {
36
42
  private val contextRef = WeakReference(context)
37
43
 
38
44
  private val contentResolver
@@ -50,7 +56,7 @@ class AssetLegacyDelegate(contentUri: Uri, context: Context) : AssetDelegate {
50
56
 
51
57
  override suspend fun getCreationTime(): Long? {
52
58
  return contentResolver
53
- .queryGetCreationTime(contentUri)
59
+ .queryAssetCreationTime(contentUri)
54
60
  .takeIf { it != 0L }
55
61
  }
56
62
 
@@ -98,7 +104,10 @@ class AssetLegacyDelegate(contentUri: Uri, context: Context) : AssetDelegate {
98
104
  MediaType.fromContentUri(contentUri)
99
105
 
100
106
  override suspend fun getModificationTime(): Long? =
101
- contentResolver.queryAssetModificationTime(contentUri).takeIf { it != 0L }
107
+ contentResolver.queryAssetModificationTime(contentUri)
108
+ ?.takeIf { it != 0L }
109
+ ?.toDuration(DurationUnit.SECONDS)
110
+ ?.inWholeMilliseconds
102
111
 
103
112
  override suspend fun getMimeType(): MimeType {
104
113
  return contentResolver.getType(contentUri)?.let { MimeType(it) }
@@ -106,12 +115,7 @@ class AssetLegacyDelegate(contentUri: Uri, context: Context) : AssetDelegate {
106
115
  }
107
116
 
108
117
  override suspend fun delete(): Unit = withContext(Dispatchers.IO) {
109
- val path = contentResolver.queryAssetPath(contentUri)
110
- ?: throw AssetPropertyNotFoundException("Uri")
111
- if (!File(path).delete()) {
112
- throw AssetFileException("Could not delete file.")
113
- }
114
- contentResolver.delete(contentUri, null, null)
118
+ assetDeleter.delete(contentUri)
115
119
  }
116
120
 
117
121
  override suspend fun getUri(): Uri {
@@ -141,6 +145,7 @@ class AssetLegacyDelegate(contentUri: Uri, context: Context) : AssetDelegate {
141
145
  if (uri == null) {
142
146
  throw AssetCouldNotBeCreated("Could not create a new asset while copying the old one")
143
147
  }
144
- return@withContext Asset(uri, contextRef.getOrThrow())
148
+ val newAssetDelegate = AssetLegacyDelegate(contentUri, assetDeleter, contextRef.getOrThrow())
149
+ return@withContext Asset(newAssetDelegate)
145
150
  }
146
151
  }
@@ -18,19 +18,26 @@ import expo.modules.medialibrary.next.extensions.resolver.queryAssetHeight
18
18
  import expo.modules.medialibrary.next.extensions.resolver.queryAssetModificationTime
19
19
  import expo.modules.medialibrary.next.extensions.resolver.queryAssetPath
20
20
  import expo.modules.medialibrary.next.extensions.resolver.queryAssetWidth
21
- import expo.modules.medialibrary.next.extensions.resolver.queryGetCreationTime
21
+ import expo.modules.medialibrary.next.extensions.resolver.queryAssetCreationTime
22
22
  import expo.modules.medialibrary.next.extensions.resolver.updateRelativePath
23
23
  import expo.modules.medialibrary.next.objects.wrappers.RelativePath
24
24
  import expo.modules.medialibrary.next.objects.asset.Asset
25
+ import expo.modules.medialibrary.next.objects.asset.deleters.AssetDeleter
25
26
  import expo.modules.medialibrary.next.objects.wrappers.MediaType
26
27
  import expo.modules.medialibrary.next.objects.wrappers.MimeType
27
28
  import kotlinx.coroutines.Dispatchers
28
29
  import kotlinx.coroutines.withContext
29
30
  import java.io.File
30
31
  import java.lang.ref.WeakReference
32
+ import kotlin.time.DurationUnit
33
+ import kotlin.time.toDuration
31
34
 
32
35
  @RequiresApi(Build.VERSION_CODES.Q)
33
- class AssetModernDelegate(override val contentUri: Uri, context: Context) : AssetDelegate {
36
+ class AssetModernDelegate(
37
+ override val contentUri: Uri,
38
+ val assetDeleter: AssetDeleter,
39
+ context: Context
40
+ ) : AssetDelegate {
34
41
  private val contextRef = WeakReference(context)
35
42
 
36
43
  private val contentResolver
@@ -40,7 +47,7 @@ class AssetModernDelegate(override val contentUri: Uri, context: Context) : Asse
40
47
 
41
48
  override suspend fun getCreationTime(): Long? {
42
49
  return contentResolver
43
- .queryGetCreationTime(contentUri)
50
+ .queryAssetCreationTime(contentUri)
44
51
  .takeIf { it != 0L }
45
52
  }
46
53
 
@@ -88,7 +95,10 @@ class AssetModernDelegate(override val contentUri: Uri, context: Context) : Asse
88
95
  MediaType.fromContentUri(contentUri)
89
96
 
90
97
  override suspend fun getModificationTime(): Long? =
91
- contentResolver.queryAssetModificationTime(contentUri).takeIf { it != 0L }
98
+ contentResolver.queryAssetModificationTime(contentUri)
99
+ ?.takeIf { it != 0L }
100
+ ?.toDuration(DurationUnit.SECONDS)
101
+ ?.inWholeMilliseconds
92
102
 
93
103
  override suspend fun getUri(): Uri {
94
104
  // e.g. storage/emulated/0/Android/data/expo/files/[ROOT_ALBUM]/[ALBUM_NAME]
@@ -104,8 +114,8 @@ class AssetModernDelegate(override val contentUri: Uri, context: Context) : Asse
104
114
  ?: MimeType.from(getUri())
105
115
  }
106
116
 
107
- override suspend fun delete() {
108
- contentResolver.delete(contentUri, null, null)
117
+ override suspend fun delete() = withContext(Dispatchers.IO) {
118
+ assetDeleter.delete(contentUri)
109
119
  }
110
120
 
111
121
  override suspend fun move(relativePath: RelativePath) {
@@ -116,6 +126,7 @@ class AssetModernDelegate(override val contentUri: Uri, context: Context) : Asse
116
126
  val newAssetUri = contentResolver.insertPendingAsset(getFilename(), getMimeType(), relativePath)
117
127
  contentResolver.copyUriContent(contentUri, newAssetUri)
118
128
  contentResolver.publishPendingAsset(newAssetUri)
119
- return@withContext Asset(newAssetUri, contextRef.getOrThrow())
129
+ val newAssetDelegate = AssetModernDelegate(newAssetUri, assetDeleter, contextRef.getOrThrow())
130
+ return@withContext Asset(newAssetDelegate)
120
131
  }
121
132
  }