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.
- package/CHANGELOG.md +10 -0
- package/android/build.gradle +6 -6
- package/android/src/main/java/expo/modules/medialibrary/next/MediaLibraryNextModule.kt +48 -29
- package/android/src/main/java/expo/modules/medialibrary/next/extensions/resolver/AlbumExtensions.kt +9 -0
- package/android/src/main/java/expo/modules/medialibrary/next/extensions/resolver/AssetExtensions.kt +2 -5
- package/android/src/main/java/expo/modules/medialibrary/next/extensions/resolver/CursorExtensions.kt +15 -5
- package/android/src/main/java/expo/modules/medialibrary/next/objects/album/Album.kt +16 -17
- package/android/src/main/java/expo/modules/medialibrary/next/objects/album/AlbumQuery.kt +23 -0
- package/android/src/main/java/expo/modules/medialibrary/next/objects/album/factories/AlbumFactory.kt +1 -0
- package/android/src/main/java/expo/modules/medialibrary/next/objects/album/factories/AlbumLegacyFactory.kt +13 -4
- package/android/src/main/java/expo/modules/medialibrary/next/objects/album/factories/AlbumModernFactory.kt +13 -4
- package/android/src/main/java/expo/modules/medialibrary/next/objects/asset/Asset.kt +2 -18
- package/android/src/main/java/expo/modules/medialibrary/next/objects/asset/delegates/AssetLegacyDelegate.kt +17 -12
- package/android/src/main/java/expo/modules/medialibrary/next/objects/asset/delegates/AssetModernDelegate.kt +18 -7
- package/android/src/main/java/expo/modules/medialibrary/next/objects/asset/deleters/AssetDeleter.kt +8 -0
- package/android/src/main/java/expo/modules/medialibrary/next/objects/asset/deleters/AssetLegacyDeleter.kt +46 -0
- package/android/src/main/java/expo/modules/medialibrary/next/objects/asset/deleters/AssetModernDeleter.kt +21 -0
- package/android/src/main/java/expo/modules/medialibrary/next/objects/asset/factories/AssetFactory.kt +2 -1
- package/android/src/main/java/expo/modules/medialibrary/next/objects/asset/factories/AssetLegacyFactory.kt +26 -11
- package/android/src/main/java/expo/modules/medialibrary/next/objects/asset/factories/AssetModernFactory.kt +22 -3
- package/android/src/main/java/expo/modules/medialibrary/next/objects/query/MediaStoreQueryFormatter.kt +9 -3
- package/android/src/main/java/expo/modules/medialibrary/next/objects/query/Query.kt +6 -3
- package/android/src/main/java/expo/modules/medialibrary/next/objects/wrappers/MimeType.kt +10 -3
- package/android/src/main/java/expo/modules/medialibrary/next/objects/wrappers/RelativePath.kt +2 -1
- package/android/src/main/java/expo/modules/medialibrary/next/permissions/MediaStorePermissionsDelegate.kt +12 -6
- package/build/next/index.d.ts +10 -1
- package/build/next/index.d.ts.map +1 -1
- package/build/next/index.js +16 -2
- package/build/next/index.js.map +1 -1
- package/build/next/types/Album.d.ts +38 -17
- package/build/next/types/Album.d.ts.map +1 -1
- package/build/next/types/Album.js.map +1 -1
- package/build/next/types/Asset.d.ts +22 -26
- package/build/next/types/Asset.d.ts.map +1 -1
- package/build/next/types/Asset.js.map +1 -1
- package/build/next/types/Query.d.ts +1 -0
- package/build/next/types/Query.d.ts.map +1 -1
- package/build/next/types/Query.js.map +1 -1
- package/expo-module.config.json +1 -1
- package/ios/next/MediaLibraryNextModule.swift +17 -9
- package/ios/next/extensions/DateExtensions.swift +8 -0
- package/ios/next/objects/Query/PredicateBuilder.swift +1 -1
- package/ios/next/objects/Query/Query.swift +3 -3
- package/ios/next/objects/album/Album.swift +2 -2
- package/ios/next/objects/asset/Asset.swift +14 -7
- package/ios/next/repository/AssetCollectionRepository.swift +12 -0
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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 +4 -5
- package/src/next/index.ts +19 -2
- package/src/next/types/Album.ts +39 -21
- package/src/next/types/Asset.ts +31 -20
- package/src/next/types/Query.ts +1 -0
- package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.2.0/expo.modules.medialibrary-18.2.0-sources.jar.md5 +0 -1
- package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.2.0/expo.modules.medialibrary-18.2.0-sources.jar.sha1 +0 -1
- package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.2.0/expo.modules.medialibrary-18.2.0-sources.jar.sha256 +0 -1
- package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.2.0/expo.modules.medialibrary-18.2.0-sources.jar.sha512 +0 -1
- package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.2.0/expo.modules.medialibrary-18.2.0.aar +0 -0
- package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.2.0/expo.modules.medialibrary-18.2.0.aar.md5 +0 -1
- package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.2.0/expo.modules.medialibrary-18.2.0.aar.sha1 +0 -1
- package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.2.0/expo.modules.medialibrary-18.2.0.aar.sha256 +0 -1
- package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.2.0/expo.modules.medialibrary-18.2.0.aar.sha512 +0 -1
- package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.2.0/expo.modules.medialibrary-18.2.0.module.md5 +0 -1
- package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.2.0/expo.modules.medialibrary-18.2.0.module.sha1 +0 -1
- package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.2.0/expo.modules.medialibrary-18.2.0.module.sha256 +0 -1
- package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.2.0/expo.modules.medialibrary-18.2.0.module.sha512 +0 -1
- package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.2.0/expo.modules.medialibrary-18.2.0.pom.md5 +0 -1
- package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.2.0/expo.modules.medialibrary-18.2.0.pom.sha1 +0 -1
- package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.2.0/expo.modules.medialibrary-18.2.0.pom.sha256 +0 -1
- 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
|
package/android/build.gradle
CHANGED
|
@@ -4,23 +4,23 @@ plugins {
|
|
|
4
4
|
}
|
|
5
5
|
|
|
6
6
|
group = 'host.exp.exponent'
|
|
7
|
-
version = '18.
|
|
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.
|
|
13
|
+
versionName "18.3.0-canary-20250930-9dc59d3"
|
|
14
14
|
}
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
dependencies {
|
|
18
|
-
implementation "androidx.annotation:annotation:1.
|
|
19
|
-
api "androidx.exifinterface:exifinterface:1.
|
|
20
|
-
implementation 'androidx.activity:activity-ktx: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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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,
|
|
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,
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
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 ->
|
package/android/src/main/java/expo/modules/medialibrary/next/extensions/resolver/AlbumExtensions.kt
CHANGED
|
@@ -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(
|
package/android/src/main/java/expo/modules/medialibrary/next/extensions/resolver/AssetExtensions.kt
CHANGED
|
@@ -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.
|
|
21
|
-
queryOne(contentUri, MediaStore.
|
|
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,
|
package/android/src/main/java/expo/modules/medialibrary/next/extensions/resolver/CursorExtensions.kt
CHANGED
|
@@ -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 =
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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(
|
|
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
|
-
|
|
58
|
+
suspend fun getAssets(): List<Asset> =
|
|
59
|
+
contentResolver
|
|
56
60
|
.queryAlbumAssetsContentUris(id)
|
|
57
|
-
.map {
|
|
58
|
-
}
|
|
61
|
+
.map { assetFactory.create(it) }
|
|
59
62
|
|
|
60
|
-
suspend fun delete() =
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
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
|
+
}
|
package/android/src/main/java/expo/modules/medialibrary/next/objects/album/factories/AlbumFactory.kt
CHANGED
|
@@ -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.
|
|
22
|
-
class AlbumLegacyFactory(
|
|
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.
|
|
20
|
-
class AlbumModernFactory(
|
|
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.
|
|
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.
|
|
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(
|
|
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
|
-
.
|
|
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)
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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(
|
|
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
|
-
.
|
|
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)
|
|
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
|
-
|
|
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
|
-
|
|
129
|
+
val newAssetDelegate = AssetModernDelegate(newAssetUri, assetDeleter, contextRef.getOrThrow())
|
|
130
|
+
return@withContext Asset(newAssetDelegate)
|
|
120
131
|
}
|
|
121
132
|
}
|