expo-media-library 18.3.0-canary-20251120-e46b3cc → 18.3.0-canary-20251205-756eb7a

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 (85) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/android/build.gradle +2 -2
  3. package/android/src/main/AndroidManifest.xml +2 -2
  4. package/android/src/main/java/expo/modules/medialibrary/next/MediaLibraryNextModule.kt +8 -0
  5. package/android/src/main/java/expo/modules/medialibrary/next/extensions/resolver/AssetExtensions.kt +31 -11
  6. package/android/src/main/java/expo/modules/medialibrary/next/extensions/resolver/AssetMediaStoreItem.kt +100 -0
  7. package/android/src/main/java/expo/modules/medialibrary/next/objects/asset/Asset.kt +8 -0
  8. package/android/src/main/java/expo/modules/medialibrary/next/objects/asset/delegates/AssetDelegate.kt +4 -0
  9. package/android/src/main/java/expo/modules/medialibrary/next/objects/asset/delegates/AssetLegacyDelegate.kt +61 -48
  10. package/android/src/main/java/expo/modules/medialibrary/next/objects/asset/delegates/AssetModernDelegate.kt +56 -42
  11. package/android/src/main/java/expo/modules/medialibrary/next/objects/asset/delegates/MediaStoreToAssetAdapter.kt +75 -0
  12. package/android/src/main/java/expo/modules/medialibrary/next/objects/asset/deleters/AssetLegacyDeleter.kt +2 -2
  13. package/android/src/main/java/expo/modules/medialibrary/next/records/AssetInfo.kt +18 -0
  14. package/android/src/main/java/expo/modules/medialibrary/next/records/Shape.kt +9 -0
  15. package/build/next/ExpoMediaLibraryNext.d.ts +8 -1
  16. package/build/next/ExpoMediaLibraryNext.d.ts.map +1 -1
  17. package/build/next/ExpoMediaLibraryNext.js.map +1 -1
  18. package/build/next/MediaLibraryNext.types.d.ts +1 -0
  19. package/build/next/MediaLibraryNext.types.d.ts.map +1 -1
  20. package/build/next/MediaLibraryNext.types.js +1 -0
  21. package/build/next/MediaLibraryNext.types.js.map +1 -1
  22. package/build/next/types/Asset.d.ts +14 -0
  23. package/build/next/types/Asset.d.ts.map +1 -1
  24. package/build/next/types/Asset.js.map +1 -1
  25. package/build/next/types/AssetInfo.d.ts +13 -0
  26. package/build/next/types/AssetInfo.d.ts.map +1 -0
  27. package/build/next/types/AssetInfo.js +2 -0
  28. package/build/next/types/AssetInfo.js.map +1 -0
  29. package/build/next/types/Shape.d.ts +5 -0
  30. package/build/next/types/Shape.d.ts.map +1 -0
  31. package/build/next/types/Shape.js +2 -0
  32. package/build/next/types/Shape.js.map +1 -0
  33. package/expo-module.config.json +1 -1
  34. package/ios/next/MediaLibraryNextModule.swift +8 -0
  35. package/ios/next/objects/asset/Asset.swift +22 -0
  36. package/ios/next/objects/asset/AssetInfo.swift +37 -0
  37. package/ios/next/objects/asset/Shape.swift +13 -0
  38. package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/{18.3.0-canary-20251120-e46b3cc/expo.modules.medialibrary-18.3.0-canary-20251120-e46b3cc-sources.jar → 18.3.0-canary-20251205-756eb7a/expo.modules.medialibrary-18.3.0-canary-20251205-756eb7a-sources.jar} +0 -0
  39. package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.3.0-canary-20251205-756eb7a/expo.modules.medialibrary-18.3.0-canary-20251205-756eb7a-sources.jar.md5 +1 -0
  40. package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.3.0-canary-20251205-756eb7a/expo.modules.medialibrary-18.3.0-canary-20251205-756eb7a-sources.jar.sha1 +1 -0
  41. package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.3.0-canary-20251205-756eb7a/expo.modules.medialibrary-18.3.0-canary-20251205-756eb7a-sources.jar.sha256 +1 -0
  42. package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.3.0-canary-20251205-756eb7a/expo.modules.medialibrary-18.3.0-canary-20251205-756eb7a-sources.jar.sha512 +1 -0
  43. package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.3.0-canary-20251205-756eb7a/expo.modules.medialibrary-18.3.0-canary-20251205-756eb7a.aar +0 -0
  44. package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.3.0-canary-20251205-756eb7a/expo.modules.medialibrary-18.3.0-canary-20251205-756eb7a.aar.md5 +1 -0
  45. package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.3.0-canary-20251205-756eb7a/expo.modules.medialibrary-18.3.0-canary-20251205-756eb7a.aar.sha1 +1 -0
  46. package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.3.0-canary-20251205-756eb7a/expo.modules.medialibrary-18.3.0-canary-20251205-756eb7a.aar.sha256 +1 -0
  47. package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.3.0-canary-20251205-756eb7a/expo.modules.medialibrary-18.3.0-canary-20251205-756eb7a.aar.sha512 +1 -0
  48. package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/{18.3.0-canary-20251120-e46b3cc/expo.modules.medialibrary-18.3.0-canary-20251120-e46b3cc.module → 18.3.0-canary-20251205-756eb7a/expo.modules.medialibrary-18.3.0-canary-20251205-756eb7a.module} +22 -22
  49. package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.3.0-canary-20251205-756eb7a/expo.modules.medialibrary-18.3.0-canary-20251205-756eb7a.module.md5 +1 -0
  50. package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.3.0-canary-20251205-756eb7a/expo.modules.medialibrary-18.3.0-canary-20251205-756eb7a.module.sha1 +1 -0
  51. package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.3.0-canary-20251205-756eb7a/expo.modules.medialibrary-18.3.0-canary-20251205-756eb7a.module.sha256 +1 -0
  52. package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.3.0-canary-20251205-756eb7a/expo.modules.medialibrary-18.3.0-canary-20251205-756eb7a.module.sha512 +1 -0
  53. package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/{18.3.0-canary-20251120-e46b3cc/expo.modules.medialibrary-18.3.0-canary-20251120-e46b3cc.pom → 18.3.0-canary-20251205-756eb7a/expo.modules.medialibrary-18.3.0-canary-20251205-756eb7a.pom} +1 -1
  54. package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.3.0-canary-20251205-756eb7a/expo.modules.medialibrary-18.3.0-canary-20251205-756eb7a.pom.md5 +1 -0
  55. package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.3.0-canary-20251205-756eb7a/expo.modules.medialibrary-18.3.0-canary-20251205-756eb7a.pom.sha1 +1 -0
  56. package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.3.0-canary-20251205-756eb7a/expo.modules.medialibrary-18.3.0-canary-20251205-756eb7a.pom.sha256 +1 -0
  57. package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.3.0-canary-20251205-756eb7a/expo.modules.medialibrary-18.3.0-canary-20251205-756eb7a.pom.sha512 +1 -0
  58. package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/maven-metadata.xml +4 -4
  59. package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/maven-metadata.xml.md5 +1 -1
  60. package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/maven-metadata.xml.sha1 +1 -1
  61. package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/maven-metadata.xml.sha256 +1 -1
  62. package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/maven-metadata.xml.sha512 +1 -1
  63. package/package.json +3 -3
  64. package/src/next/ExpoMediaLibraryNext.ts +14 -1
  65. package/src/next/MediaLibraryNext.types.ts +1 -0
  66. package/src/next/types/Asset.ts +16 -0
  67. package/src/next/types/AssetInfo.ts +13 -0
  68. package/src/next/types/Shape.ts +4 -0
  69. package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.3.0-canary-20251120-e46b3cc/expo.modules.medialibrary-18.3.0-canary-20251120-e46b3cc-sources.jar.md5 +0 -1
  70. package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.3.0-canary-20251120-e46b3cc/expo.modules.medialibrary-18.3.0-canary-20251120-e46b3cc-sources.jar.sha1 +0 -1
  71. package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.3.0-canary-20251120-e46b3cc/expo.modules.medialibrary-18.3.0-canary-20251120-e46b3cc-sources.jar.sha256 +0 -1
  72. package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.3.0-canary-20251120-e46b3cc/expo.modules.medialibrary-18.3.0-canary-20251120-e46b3cc-sources.jar.sha512 +0 -1
  73. package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.3.0-canary-20251120-e46b3cc/expo.modules.medialibrary-18.3.0-canary-20251120-e46b3cc.aar +0 -0
  74. package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.3.0-canary-20251120-e46b3cc/expo.modules.medialibrary-18.3.0-canary-20251120-e46b3cc.aar.md5 +0 -1
  75. package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.3.0-canary-20251120-e46b3cc/expo.modules.medialibrary-18.3.0-canary-20251120-e46b3cc.aar.sha1 +0 -1
  76. package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.3.0-canary-20251120-e46b3cc/expo.modules.medialibrary-18.3.0-canary-20251120-e46b3cc.aar.sha256 +0 -1
  77. package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.3.0-canary-20251120-e46b3cc/expo.modules.medialibrary-18.3.0-canary-20251120-e46b3cc.aar.sha512 +0 -1
  78. package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.3.0-canary-20251120-e46b3cc/expo.modules.medialibrary-18.3.0-canary-20251120-e46b3cc.module.md5 +0 -1
  79. package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.3.0-canary-20251120-e46b3cc/expo.modules.medialibrary-18.3.0-canary-20251120-e46b3cc.module.sha1 +0 -1
  80. package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.3.0-canary-20251120-e46b3cc/expo.modules.medialibrary-18.3.0-canary-20251120-e46b3cc.module.sha256 +0 -1
  81. package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.3.0-canary-20251120-e46b3cc/expo.modules.medialibrary-18.3.0-canary-20251120-e46b3cc.module.sha512 +0 -1
  82. package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.3.0-canary-20251120-e46b3cc/expo.modules.medialibrary-18.3.0-canary-20251120-e46b3cc.pom.md5 +0 -1
  83. package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.3.0-canary-20251120-e46b3cc/expo.modules.medialibrary-18.3.0-canary-20251120-e46b3cc.pom.sha1 +0 -1
  84. package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.3.0-canary-20251120-e46b3cc/expo.modules.medialibrary-18.3.0-canary-20251120-e46b3cc.pom.sha256 +0 -1
  85. package/local-maven-repo/host/exp/exponent/expo.modules.medialibrary/18.3.0-canary-20251120-e46b3cc/expo.modules.medialibrary-18.3.0-canary-20251120-e46b3cc.pom.sha512 +0 -1
package/CHANGELOG.md CHANGED
@@ -6,6 +6,8 @@
6
6
 
7
7
  ### 🎉 New features
8
8
 
9
+ - [next] Add `asset.getShape()` ([#40110](https://github.com/expo/expo/pull/40110) by [@Wenszel](https://github.com/Wenszel))
10
+ - [next] Add `asset.getInfo()` ([#40146](https://github.com/expo/expo/pull/40146) by [@Wenszel](https://github.com/Wenszel))
9
11
  - [next] Add exif support ([#39992](https://github.com/expo/expo/pull/39992) by [@Wenszel](https://github.com/Wenszel))
10
12
  - [next] Add `Album.get(title)` ([#39717](https://github.com/expo/expo/pull/39717) by [@Wenszel](https://github.com/Wenszel))
11
13
 
@@ -23,6 +25,11 @@
23
25
  - [next] Add test screens ([#39951](https://github.com/expo/expo/pull/39951) by [@Wenszel](https://github.com/Wenszel))
24
26
  - [next] Add documentation ([#39754](https://github.com/expo/expo/pull/39754) by [@Wenszel](https://github.com/Wenszel))
25
27
  - Remove tests related files from the published package content. ([#39551](https://github.com/expo/expo/pull/39551) by [@Simek](https://github.com/Simek))
28
+ - [Android] Add `android:maxSdkVersion` annotation to `READ_EXTERNAL_STORAGE` and `WRITE_EXTERNAL_STORAGE` permissions. ([#40976](https://github.com/expo/expo/pull/40976) by [@behenate](https://github.com/behenate))
29
+
30
+ ## 18.2.1 - 2025-12-05
31
+
32
+ _This version does not introduce any user-facing changes._
26
33
 
27
34
  ## 18.2.0 — 2025-09-16
28
35
 
@@ -4,13 +4,13 @@ plugins {
4
4
  }
5
5
 
6
6
  group = 'host.exp.exponent'
7
- version = '18.3.0-canary-20251120-e46b3cc'
7
+ version = '18.3.0-canary-20251205-756eb7a'
8
8
 
9
9
  android {
10
10
  namespace "expo.modules.medialibrary"
11
11
  defaultConfig {
12
12
  versionCode 37
13
- versionName "18.3.0-canary-20251120-e46b3cc"
13
+ versionName "18.3.0-canary-20251205-756eb7a"
14
14
  }
15
15
  }
16
16
 
@@ -1,5 +1,5 @@
1
1
  <manifest xmlns:android="http://schemas.android.com/apk/res/android">
2
2
  <uses-permission android:name="android.permission.READ_MEDIA_VISUAL_USER_SELECTED" />
3
- <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
4
- <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
3
+ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
4
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
5
5
  </manifest>
@@ -93,6 +93,10 @@ class MediaLibraryNextModule : Module() {
93
93
  self.getExif()
94
94
  }
95
95
 
96
+ AsyncFunction("getInfo") Coroutine { self: Asset ->
97
+ self.getInfo()
98
+ }
99
+
96
100
  AsyncFunction("getLocation") Coroutine { self: Asset ->
97
101
  self.getLocation()
98
102
  }
@@ -113,6 +117,10 @@ class MediaLibraryNextModule : Module() {
113
117
  self.getModificationTime()
114
118
  }
115
119
 
120
+ AsyncFunction("getShape") Coroutine { self: Asset ->
121
+ self.getShape()
122
+ }
123
+
116
124
  AsyncFunction("getUri") Coroutine { self: Asset ->
117
125
  self.getUri()
118
126
  }
@@ -9,34 +9,54 @@ import android.provider.MediaStore
9
9
  import androidx.annotation.RequiresApi
10
10
  import expo.modules.medialibrary.EXTERNAL_CONTENT_URI
11
11
  import expo.modules.medialibrary.next.exceptions.AssetCouldNotBeCreated
12
+ import expo.modules.medialibrary.next.extensions.resolver.AssetMediaStoreItemBuilder.Companion.buildAssetMediaStoreItem
13
+ import expo.modules.medialibrary.next.objects.wrappers.MediaType
12
14
  import expo.modules.medialibrary.next.objects.wrappers.RelativePath
13
15
  import expo.modules.medialibrary.next.objects.wrappers.MimeType
14
16
  import kotlinx.coroutines.Dispatchers
15
17
  import kotlinx.coroutines.withContext
16
18
 
17
19
  suspend fun ContentResolver.queryAssetDisplayName(contentUri: Uri): String? =
18
- queryOne(contentUri, MediaStore.MediaColumns.DISPLAY_NAME, Cursor::getString)
20
+ queryOne(contentUri, AssetMediaStoreProperty.DisplayName.column, Cursor::getString)
19
21
 
20
- suspend fun ContentResolver.queryAssetCreationTime(contentUri: Uri): Long? =
21
- queryOne(contentUri, MediaStore.Images.Media.DATE_TAKEN, Cursor::getLong)
22
+ suspend fun ContentResolver.queryAssetDateTaken(contentUri: Uri): Long? =
23
+ queryOne(contentUri, AssetMediaStoreProperty.DateTaken.column, Cursor::getLong)
22
24
 
23
- suspend fun ContentResolver.queryAssetModificationTime(contentUri: Uri): Long? =
24
- queryOne(contentUri, MediaStore.MediaColumns.DATE_MODIFIED, Cursor::getLong)
25
+ suspend fun ContentResolver.queryAssetDateModified(contentUri: Uri): Long? =
26
+ queryOne(contentUri, AssetMediaStoreProperty.DateModified.column, Cursor::getLong)
25
27
 
26
28
  suspend fun ContentResolver.queryAssetDuration(contentUri: Uri): Long? =
27
- queryOne(contentUri, MediaStore.Video.VideoColumns.DURATION, Cursor::getLong)
29
+ queryOne(contentUri, AssetMediaStoreProperty.Duration.column, Cursor::getLong)
28
30
 
29
31
  suspend fun ContentResolver.queryAssetWidth(contentUri: Uri): Int? =
30
- queryOne(contentUri, MediaStore.MediaColumns.WIDTH, Cursor::getInt)
32
+ queryOne(contentUri, AssetMediaStoreProperty.Width.column, Cursor::getInt)
31
33
 
32
34
  suspend fun ContentResolver.queryAssetHeight(contentUri: Uri): Int? =
33
- queryOne(contentUri, MediaStore.MediaColumns.HEIGHT, Cursor::getInt)
35
+ queryOne(contentUri, AssetMediaStoreProperty.Height.column, Cursor::getInt)
34
36
 
35
- suspend fun ContentResolver.queryAssetPath(contentUri: Uri): String? =
36
- queryOne(contentUri, MediaStore.Files.FileColumns.DATA, Cursor::getString)
37
+ suspend fun ContentResolver.queryAssetData(contentUri: Uri): String? =
38
+ queryOne(contentUri, AssetMediaStoreProperty.Data.column, Cursor::getString)
37
39
 
38
40
  suspend fun ContentResolver.queryAssetBucketId(contentUri: Uri): Int? =
39
- queryOne(contentUri, MediaStore.MediaColumns.BUCKET_ID, Cursor::getInt)
41
+ queryOne(contentUri, AssetMediaStoreProperty.BucketId.column, Cursor::getInt)
42
+
43
+ suspend fun ContentResolver.queryAssetMediaStoreItem(
44
+ contentUri: Uri
45
+ ): AssetMediaStoreItem? = withContext(Dispatchers.IO) {
46
+ // Attempting to get a duration from an image may result in an exception on older Android versions
47
+ val includeDuration = MediaType.fromContentUri(contentUri) != MediaType.IMAGE
48
+ val projection = AssetMediaStoreProperty.projection(includeDuration)
49
+ val cursor = safeQuery(contentUri, projection, null, null)
50
+ ?: return@withContext null
51
+
52
+ return@withContext cursor.use {
53
+ if (it.moveToFirst()) {
54
+ it.buildAssetMediaStoreItem(includeDuration)
55
+ } else {
56
+ null
57
+ }
58
+ }
59
+ }
40
60
 
41
61
  suspend fun ContentResolver.insertPendingAsset(
42
62
  displayName: String,
@@ -0,0 +1,100 @@
1
+ package expo.modules.medialibrary.next.extensions.resolver
2
+
3
+ import android.database.Cursor
4
+ import android.provider.MediaStore
5
+
6
+ data class AssetMediaStoreItem(
7
+ val displayName: String?,
8
+ val height: Int?,
9
+ val width: Int?,
10
+ val dateTaken: Long?,
11
+ val dateModified: Long?,
12
+ val duration: Long?,
13
+ val data: String?,
14
+ val bucketId: String?
15
+ )
16
+
17
+ class AssetMediaStoreItemBuilder {
18
+ private var displayName: String? = null
19
+ private var height: Int? = null
20
+ private var width: Int? = null
21
+ private var dateTaken: Long? = null
22
+ private var dateModified: Long? = null
23
+ private var duration: Long? = null
24
+ private var data: String? = null
25
+ private var bucketId: String? = null
26
+
27
+ fun build(): AssetMediaStoreItem {
28
+ return AssetMediaStoreItem(
29
+ displayName = displayName,
30
+ height = height,
31
+ width = width,
32
+ dateTaken = dateTaken,
33
+ dateModified = dateModified,
34
+ duration = duration,
35
+ data = data,
36
+ bucketId = bucketId
37
+ )
38
+ }
39
+
40
+ fun Cursor.set(property: AssetMediaStoreProperty) {
41
+ with(property) {
42
+ when (property) {
43
+ AssetMediaStoreProperty.Data -> data = getString()
44
+ AssetMediaStoreProperty.DisplayName -> displayName = getString()
45
+ AssetMediaStoreProperty.Height -> height = getInt()
46
+ AssetMediaStoreProperty.Width -> width = getInt()
47
+ AssetMediaStoreProperty.DateTaken -> dateTaken = getLong()
48
+ AssetMediaStoreProperty.DateModified -> dateModified = getLong()
49
+ AssetMediaStoreProperty.Duration -> duration = getLong()
50
+ AssetMediaStoreProperty.BucketId -> bucketId = getString()
51
+ }
52
+ }
53
+ }
54
+
55
+ companion object {
56
+ fun Cursor.buildAssetMediaStoreItem(includeDuration: Boolean): AssetMediaStoreItem {
57
+ val assetMediaStoreItemBuilder = AssetMediaStoreItemBuilder()
58
+ AssetMediaStoreProperty
59
+ .values()
60
+ .filter { includeDuration || it != AssetMediaStoreProperty.Duration }
61
+ .forEach {
62
+ with(assetMediaStoreItemBuilder) {
63
+ set(it)
64
+ }
65
+ }
66
+ return assetMediaStoreItemBuilder.build()
67
+ }
68
+ }
69
+ }
70
+
71
+ enum class AssetMediaStoreProperty(val column: String) {
72
+ DisplayName(MediaStore.Files.FileColumns.DISPLAY_NAME),
73
+ Height(MediaStore.MediaColumns.HEIGHT),
74
+ Width(MediaStore.MediaColumns.WIDTH),
75
+ DateTaken(MediaStore.Images.ImageColumns.DATE_TAKEN),
76
+ DateModified(MediaStore.MediaColumns.DATE_MODIFIED),
77
+ Duration(MediaStore.MediaColumns.DURATION),
78
+ Data(MediaStore.MediaColumns.DATA),
79
+ BucketId(MediaStore.MediaColumns.BUCKET_ID);
80
+
81
+ fun Cursor.columnIndex(): Int =
82
+ getColumnIndexOrThrow(column)
83
+
84
+ fun Cursor.getString(): String? =
85
+ getString(columnIndex())
86
+
87
+ fun Cursor.getInt(): Int? =
88
+ getInt(columnIndex())
89
+
90
+ fun Cursor.getLong(): Long? =
91
+ getLong(columnIndex())
92
+
93
+ companion object {
94
+ fun projection(includeDuration: Boolean): Array<String> =
95
+ values()
96
+ .filter { includeDuration || it != Duration }
97
+ .map { it.column }
98
+ .toTypedArray()
99
+ }
100
+ }
@@ -7,7 +7,9 @@ import expo.modules.medialibrary.next.objects.asset.delegates.AssetDelegate
7
7
  import expo.modules.medialibrary.next.objects.wrappers.RelativePath
8
8
  import expo.modules.medialibrary.next.objects.wrappers.MediaType
9
9
  import expo.modules.medialibrary.next.objects.wrappers.MimeType
10
+ import expo.modules.medialibrary.next.records.AssetInfo
10
11
  import expo.modules.medialibrary.next.records.Location
12
+ import expo.modules.medialibrary.next.records.Shape
11
13
  import kotlinx.coroutines.Dispatchers
12
14
  import kotlinx.coroutines.withContext
13
15
 
@@ -29,6 +31,9 @@ class Asset(val assetDelegate: AssetDelegate) : SharedObject() {
29
31
  suspend fun getWidth(): Int =
30
32
  assetDelegate.getWidth()
31
33
 
34
+ suspend fun getShape(): Shape? =
35
+ assetDelegate.getShape()
36
+
32
37
  suspend fun getMediaType(): MediaType =
33
38
  assetDelegate.getMediaType()
34
39
 
@@ -38,6 +43,9 @@ class Asset(val assetDelegate: AssetDelegate) : SharedObject() {
38
43
  suspend fun getUri(): Uri =
39
44
  assetDelegate.getUri()
40
45
 
46
+ suspend fun getInfo(): AssetInfo =
47
+ assetDelegate.getInfo()
48
+
41
49
  suspend fun getMimeType(): MimeType =
42
50
  assetDelegate.getMimeType()
43
51
 
@@ -6,7 +6,9 @@ import expo.modules.medialibrary.next.objects.wrappers.RelativePath
6
6
  import expo.modules.medialibrary.next.objects.asset.Asset
7
7
  import expo.modules.medialibrary.next.objects.wrappers.MediaType
8
8
  import expo.modules.medialibrary.next.objects.wrappers.MimeType
9
+ import expo.modules.medialibrary.next.records.AssetInfo
9
10
  import expo.modules.medialibrary.next.records.Location
11
+ import expo.modules.medialibrary.next.records.Shape
10
12
 
11
13
  interface AssetDelegate {
12
14
  val contentUri: Uri
@@ -15,9 +17,11 @@ interface AssetDelegate {
15
17
  suspend fun getFilename(): String
16
18
  suspend fun getHeight(): Int
17
19
  suspend fun getWidth(): Int
20
+ suspend fun getShape(): Shape?
18
21
  suspend fun getMediaType(): MediaType
19
22
  suspend fun getModificationTime(): Long?
20
23
  suspend fun getUri(): Uri
24
+ suspend fun getInfo(): AssetInfo
21
25
  suspend fun getMimeType(): MimeType
22
26
  suspend fun getLocation(): Location?
23
27
  suspend fun getExif(): Bundle
@@ -1,12 +1,10 @@
1
1
  package expo.modules.medialibrary.next.objects.asset.delegates
2
2
 
3
3
  import android.content.Context
4
- import android.graphics.BitmapFactory
5
4
  import android.net.Uri
6
5
  import android.os.Build
7
6
  import android.os.Bundle
8
7
  import androidx.annotation.DeprecatedSinceApi
9
- import androidx.core.net.toUri
10
8
  import androidx.exifinterface.media.ExifInterface
11
9
  import expo.modules.medialibrary.next.exceptions.AssetCouldNotBeCreated
12
10
  import expo.modules.medialibrary.next.exceptions.AssetPropertyNotFoundException
@@ -16,10 +14,11 @@ import expo.modules.medialibrary.next.extensions.resolver.deleteBy
16
14
  import expo.modules.medialibrary.next.extensions.resolver.queryAssetDisplayName
17
15
  import expo.modules.medialibrary.next.extensions.resolver.queryAssetDuration
18
16
  import expo.modules.medialibrary.next.extensions.resolver.queryAssetHeight
19
- import expo.modules.medialibrary.next.extensions.resolver.queryAssetModificationTime
20
- import expo.modules.medialibrary.next.extensions.resolver.queryAssetPath
21
17
  import expo.modules.medialibrary.next.extensions.resolver.queryAssetWidth
22
- import expo.modules.medialibrary.next.extensions.resolver.queryAssetCreationTime
18
+ import expo.modules.medialibrary.next.extensions.resolver.queryAssetData
19
+ import expo.modules.medialibrary.next.extensions.resolver.queryAssetDateModified
20
+ import expo.modules.medialibrary.next.extensions.resolver.queryAssetDateTaken
21
+ import expo.modules.medialibrary.next.extensions.resolver.queryAssetMediaStoreItem
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.extensions.scanFile
@@ -30,7 +29,9 @@ import expo.modules.medialibrary.next.objects.asset.deleters.AssetDeleter
30
29
  import expo.modules.medialibrary.next.objects.wrappers.MediaType
31
30
  import expo.modules.medialibrary.next.objects.wrappers.MimeType
32
31
  import expo.modules.medialibrary.next.permissions.SystemPermissionsDelegate
32
+ import expo.modules.medialibrary.next.records.AssetInfo
33
33
  import expo.modules.medialibrary.next.records.Location
34
+ import expo.modules.medialibrary.next.records.Shape
34
35
  import kotlinx.coroutines.Dispatchers
35
36
  import kotlinx.coroutines.ensureActive
36
37
  import kotlinx.coroutines.withContext
@@ -38,8 +39,6 @@ import java.io.File
38
39
  import java.lang.ref.WeakReference
39
40
  import kotlin.collections.component1
40
41
  import kotlin.collections.component2
41
- import kotlin.time.DurationUnit
42
- import kotlin.time.toDuration
43
42
 
44
43
  @DeprecatedSinceApi(Build.VERSION_CODES.Q)
45
44
  class AssetLegacyDelegate(
@@ -63,20 +62,21 @@ class AssetLegacyDelegate(
63
62
  override var contentUri: Uri = contentUri
64
63
  private set
65
64
 
65
+ private val mediaStoreToAssetAdapter by lazy {
66
+ MediaStoreToAssetAdapter(contextRef.getOrThrow())
67
+ }
68
+
66
69
  override suspend fun getCreationTime(): Long? {
67
- return contentResolver
68
- .queryAssetCreationTime(contentUri)
69
- .takeIf { it != 0L }
70
+ val mediaStoreDateTaken = contentResolver.queryAssetDateTaken(contentUri)
71
+ return mediaStoreToAssetAdapter.transformCreationTime(mediaStoreDateTaken)
70
72
  }
71
73
 
72
74
  override suspend fun getDuration(): Long? {
73
- return if (getMimeType().isVideo()) {
74
- contentResolver
75
- .queryAssetDuration(contentUri)
76
- .takeIf { it != 0L }
77
- } else {
78
- null
75
+ if (getMediaType() != MediaType.VIDEO) {
76
+ return null
79
77
  }
78
+ val mediaStoreDuration = contentResolver.queryAssetDuration(contentUri)
79
+ return mediaStoreToAssetAdapter.transformDuration(mediaStoreDuration)
80
80
  }
81
81
 
82
82
  override suspend fun getFilename(): String =
@@ -84,39 +84,61 @@ class AssetLegacyDelegate(
84
84
  ?: throw AssetPropertyNotFoundException("Filename")
85
85
 
86
86
  override suspend fun getHeight(): Int {
87
- val height = contentResolver.queryAssetHeight(contentUri)
87
+ val mediaStoreHeight = contentResolver.queryAssetHeight(contentUri)
88
+ return mediaStoreToAssetAdapter.transformHeight(mediaStoreHeight, contentUri)
88
89
  ?: throw AssetPropertyNotFoundException("Height")
89
- // If height is not saved to the database
90
- if (getMediaType() == MediaType.IMAGE && height <= 0) {
91
- return downloadBitmapAndGet { it.outHeight }
92
- }
93
- return height
94
90
  }
95
91
 
96
92
  override suspend fun getWidth(): Int {
97
- val width = contentResolver.queryAssetWidth(contentUri)
93
+ val mediaStoreWidth = contentResolver.queryAssetWidth(contentUri)
94
+ return mediaStoreToAssetAdapter.transformWidth(mediaStoreWidth, contentUri)
98
95
  ?: throw AssetPropertyNotFoundException("Width")
99
- if (getMediaType() == MediaType.IMAGE && width <= 0) {
100
- return downloadBitmapAndGet { it.outWidth }
101
- }
102
- return width
103
96
  }
104
97
 
105
- private suspend fun downloadBitmapAndGet(extract: (BitmapFactory.Options) -> Int): Int {
106
- val options = BitmapFactory.Options().apply { inJustDecodeBounds = true }
107
- val stringAssetUri = contentResolver.queryAssetPath(contentUri)
108
- BitmapFactory.decodeFile(stringAssetUri, options)
109
- return extract(options)
98
+ override suspend fun getShape(): Shape? {
99
+ val width = getWidth()
100
+ val height = getHeight()
101
+ return Shape(width, height).takeIf { width > 0 && height > 0 }
110
102
  }
111
103
 
112
104
  override suspend fun getMediaType(): MediaType =
113
105
  MediaType.fromContentUri(contentUri)
114
106
 
115
- override suspend fun getModificationTime(): Long? =
116
- contentResolver.queryAssetModificationTime(contentUri)
117
- ?.takeIf { it != 0L }
118
- ?.toDuration(DurationUnit.SECONDS)
119
- ?.inWholeMilliseconds
107
+ override suspend fun getModificationTime(): Long? {
108
+ val mediaStoreDateModified = contentResolver.queryAssetDateModified(contentUri)
109
+ return mediaStoreToAssetAdapter.transformModificationTime(mediaStoreDateModified)
110
+ }
111
+
112
+ override suspend fun getUri(): Uri {
113
+ // e.g. storage/emulated/0/Android/data/expo/files/[ROOT_ALBUM]/[ALBUM_NAME]
114
+ val mediaStoreData = contentResolver.queryAssetData(contentUri)
115
+ // e.g. file:///storage/emulated/0/Android/data/expo/files/[ROOT_ALBUM]/[ALBUM_NAME]
116
+ return mediaStoreToAssetAdapter.transformUri(mediaStoreData)
117
+ ?: throw AssetPropertyNotFoundException("Uri")
118
+ }
119
+
120
+ override suspend fun getInfo(): AssetInfo {
121
+ val mediaStoreItem = contentResolver.queryAssetMediaStoreItem(contentUri)
122
+ ?: throw AssetPropertyNotFoundException("Info")
123
+ val mediaType = getMediaType()
124
+ val height = mediaStoreToAssetAdapter.transformHeight(mediaStoreItem.height, contentUri)
125
+ val width = mediaStoreToAssetAdapter.transformWidth(mediaStoreItem.width, contentUri)
126
+ return AssetInfo(
127
+ id = contentUri,
128
+ mediaType = mediaType,
129
+ creationTime = mediaStoreToAssetAdapter.transformCreationTime(mediaStoreItem.dateTaken),
130
+ modificationTime = mediaStoreToAssetAdapter.transformModificationTime(mediaStoreItem.dateModified),
131
+ duration = mediaStoreToAssetAdapter.transformDuration(mediaStoreItem.duration),
132
+ filename = mediaStoreItem.displayName
133
+ ?: throw AssetPropertyNotFoundException("Filename"),
134
+ height = height
135
+ ?: throw AssetPropertyNotFoundException("Height"),
136
+ width = width
137
+ ?: throw AssetPropertyNotFoundException("Width"),
138
+ uri = mediaStoreToAssetAdapter.transformUri(mediaStoreItem.data)
139
+ ?: throw AssetPropertyNotFoundException("Uri")
140
+ )
141
+ }
120
142
 
121
143
  override suspend fun getMimeType(): MimeType {
122
144
  return contentResolver.getType(contentUri)?.let { MimeType(it) }
@@ -158,18 +180,9 @@ class AssetLegacyDelegate(
158
180
  assetDeleter.delete(contentUri)
159
181
  }
160
182
 
161
- override suspend fun getUri(): Uri {
162
- // e.g. storage/emulated/0/Android/data/expo/files/[ROOT_ALBUM]/[ALBUM_NAME]
163
- val path = contentResolver.queryAssetPath(contentUri)
164
- ?: throw AssetPropertyNotFoundException("Uri")
165
- // e.g. file:///storage/emulated/0/Android/data/expo/files/[ROOT_ALBUM]/[ALBUM_NAME]
166
- val uri = File(path).toUri()
167
- return uri
168
- }
169
-
170
183
  override suspend fun move(relativePath: RelativePath) = withContext(Dispatchers.IO) {
171
184
  systemPermissionsDelegate.requireWritePermissions()
172
- val path = contentResolver.queryAssetPath(contentUri)
185
+ val path = contentResolver.queryAssetData(contentUri)
173
186
  ?: throw AssetPropertyNotFoundException("Asset path")
174
187
  val newFile = File(path).safeMove(File(relativePath.toFilePath()))
175
188
  contentResolver.deleteBy(path)
@@ -179,7 +192,7 @@ class AssetLegacyDelegate(
179
192
  }
180
193
 
181
194
  override suspend fun copy(relativePath: RelativePath): Asset = withContext(Dispatchers.IO) {
182
- val path = contentResolver.queryAssetPath(contentUri)
195
+ val path = contentResolver.queryAssetData(contentUri)
183
196
  ?: throw AssetPropertyNotFoundException("Asset path")
184
197
  val newFile = File(path).safeCopy(File(relativePath.toFilePath()))
185
198
  val (_, uri) = contextRef.getOrThrow().scanFile(newFile.path, null)
@@ -1,13 +1,11 @@
1
1
  package expo.modules.medialibrary.next.objects.asset.delegates
2
2
 
3
3
  import android.content.Context
4
- import android.graphics.BitmapFactory
5
4
  import androidx.exifinterface.media.ExifInterface
6
5
  import android.net.Uri
7
6
  import android.os.Build
8
7
  import android.os.Bundle
9
8
  import androidx.annotation.RequiresApi
10
- import androidx.core.net.toUri
11
9
  import expo.modules.medialibrary.next.exceptions.AssetPropertyNotFoundException
12
10
  import expo.modules.medialibrary.next.exceptions.ContentResolverNotObtainedException
13
11
  import expo.modules.medialibrary.next.extensions.getOrThrow
@@ -17,10 +15,11 @@ import expo.modules.medialibrary.next.extensions.resolver.publishPendingAsset
17
15
  import expo.modules.medialibrary.next.extensions.resolver.queryAssetDisplayName
18
16
  import expo.modules.medialibrary.next.extensions.resolver.queryAssetDuration
19
17
  import expo.modules.medialibrary.next.extensions.resolver.queryAssetHeight
20
- import expo.modules.medialibrary.next.extensions.resolver.queryAssetModificationTime
21
- import expo.modules.medialibrary.next.extensions.resolver.queryAssetPath
22
18
  import expo.modules.medialibrary.next.extensions.resolver.queryAssetWidth
23
- import expo.modules.medialibrary.next.extensions.resolver.queryAssetCreationTime
19
+ import expo.modules.medialibrary.next.extensions.resolver.queryAssetData
20
+ import expo.modules.medialibrary.next.extensions.resolver.queryAssetDateModified
21
+ import expo.modules.medialibrary.next.extensions.resolver.queryAssetDateTaken
22
+ import expo.modules.medialibrary.next.extensions.resolver.queryAssetMediaStoreItem
24
23
  import expo.modules.medialibrary.next.extensions.resolver.updateRelativePath
25
24
  import expo.modules.medialibrary.next.objects.wrappers.RelativePath
26
25
  import expo.modules.medialibrary.next.objects.asset.Asset
@@ -29,17 +28,16 @@ import expo.modules.medialibrary.next.objects.asset.deleters.AssetDeleter
29
28
  import expo.modules.medialibrary.next.objects.wrappers.MediaType
30
29
  import expo.modules.medialibrary.next.objects.wrappers.MimeType
31
30
  import expo.modules.medialibrary.next.permissions.MediaStorePermissionsDelegate
31
+ import expo.modules.medialibrary.next.records.AssetInfo
32
32
  import expo.modules.medialibrary.next.records.Location
33
+ import expo.modules.medialibrary.next.records.Shape
33
34
  import kotlinx.coroutines.Dispatchers
34
35
  import kotlinx.coroutines.ensureActive
35
36
  import kotlinx.coroutines.withContext
36
- import java.io.File
37
37
  import java.lang.ref.WeakReference
38
38
  import kotlin.collections.component1
39
39
  import kotlin.collections.component2
40
40
  import kotlin.let
41
- import kotlin.time.DurationUnit
42
- import kotlin.time.toDuration
43
41
 
44
42
  @RequiresApi(Build.VERSION_CODES.Q)
45
43
  class AssetModernDelegate(
@@ -55,20 +53,21 @@ class AssetModernDelegate(
55
53
  .getOrThrow()
56
54
  .contentResolver ?: throw ContentResolverNotObtainedException()
57
55
 
56
+ private val mediaStoreToAssetAdapter by lazy {
57
+ MediaStoreToAssetAdapter(contextRef.getOrThrow())
58
+ }
59
+
58
60
  override suspend fun getCreationTime(): Long? {
59
- return contentResolver
60
- .queryAssetCreationTime(contentUri)
61
- .takeIf { it != 0L }
61
+ val mediaStoreDateTaken = contentResolver.queryAssetDateTaken(contentUri)
62
+ return mediaStoreToAssetAdapter.transformCreationTime(mediaStoreDateTaken)
62
63
  }
63
64
 
64
65
  override suspend fun getDuration(): Long? {
65
- return if (getMimeType().isVideo()) {
66
- contentResolver
67
- .queryAssetDuration(contentUri)
68
- .takeIf { it != 0L }
69
- } else {
70
- null
66
+ if (getMediaType() != MediaType.VIDEO) {
67
+ return null
71
68
  }
69
+ val mediaStoreDuration = contentResolver.queryAssetDuration(contentUri)
70
+ return mediaStoreToAssetAdapter.transformDuration(mediaStoreDuration)
72
71
  }
73
72
 
74
73
  override suspend fun getFilename(): String =
@@ -76,45 +75,60 @@ class AssetModernDelegate(
76
75
  ?: throw AssetPropertyNotFoundException("Filename")
77
76
 
78
77
  override suspend fun getHeight(): Int {
79
- val height = contentResolver.queryAssetHeight(contentUri)
80
- // If height is not saved to the database
81
- if (getMediaType() == MediaType.IMAGE && height != null && height <= 0) {
82
- return downloadBitmapAndGet { it.outHeight }
83
- }
84
- return height ?: throw AssetPropertyNotFoundException("Height")
78
+ val mediaStoreHeight = contentResolver.queryAssetHeight(contentUri)
79
+ return mediaStoreToAssetAdapter.transformHeight(mediaStoreHeight, contentUri)
80
+ ?: throw AssetPropertyNotFoundException("Height")
85
81
  }
86
82
 
87
83
  override suspend fun getWidth(): Int {
88
- val width = contentResolver.queryAssetWidth(contentUri)
89
- if (getMediaType() == MediaType.IMAGE && width != null && width <= 0) {
90
- return downloadBitmapAndGet { it.outWidth }
91
- }
92
- return width ?: throw AssetPropertyNotFoundException("Width")
84
+ val mediaStoreWidth = contentResolver.queryAssetWidth(contentUri)
85
+ return mediaStoreToAssetAdapter.transformWidth(mediaStoreWidth, contentUri)
86
+ ?: throw AssetPropertyNotFoundException("Width")
93
87
  }
94
88
 
95
- private suspend fun downloadBitmapAndGet(extract: (BitmapFactory.Options) -> Int): Int {
96
- val options = BitmapFactory.Options().apply { inJustDecodeBounds = true }
97
- val stringAssetUri = contentResolver.queryAssetPath(contentUri)
98
- BitmapFactory.decodeFile(stringAssetUri, options)
99
- return extract(options)
89
+ override suspend fun getShape(): Shape? {
90
+ val width = getWidth()
91
+ val height = getHeight()
92
+ return Shape(width, height).takeIf { width > 0 && height > 0 }
100
93
  }
101
94
 
102
95
  override suspend fun getMediaType(): MediaType =
103
96
  MediaType.fromContentUri(contentUri)
104
97
 
105
- override suspend fun getModificationTime(): Long? =
106
- contentResolver.queryAssetModificationTime(contentUri)
107
- ?.takeIf { it != 0L }
108
- ?.toDuration(DurationUnit.SECONDS)
109
- ?.inWholeMilliseconds
98
+ override suspend fun getModificationTime(): Long? {
99
+ val mediaStoreDateModified = contentResolver.queryAssetDateModified(contentUri)
100
+ return mediaStoreToAssetAdapter.transformModificationTime(mediaStoreDateModified)
101
+ }
110
102
 
111
103
  override suspend fun getUri(): Uri {
112
104
  // e.g. storage/emulated/0/Android/data/expo/files/[ROOT_ALBUM]/[ALBUM_NAME]
113
- val path = contentResolver.queryAssetPath(contentUri)
114
- ?: throw AssetPropertyNotFoundException("Uri")
105
+ val mediaStoreData = contentResolver.queryAssetData(contentUri)
115
106
  // e.g. file:///storage/emulated/0/Android/data/expo/files/[ROOT_ALBUM]/[ALBUM_NAME]
116
- val uri = File(path).toUri()
117
- return uri
107
+ return mediaStoreToAssetAdapter.transformUri(mediaStoreData)
108
+ ?: throw AssetPropertyNotFoundException("Uri")
109
+ }
110
+
111
+ override suspend fun getInfo(): AssetInfo {
112
+ val mediaStoreItem = contentResolver.queryAssetMediaStoreItem(contentUri)
113
+ ?: throw AssetPropertyNotFoundException("Info")
114
+ val mediaType = getMediaType()
115
+ val height = mediaStoreToAssetAdapter.transformHeight(mediaStoreItem.height, contentUri)
116
+ val width = mediaStoreToAssetAdapter.transformWidth(mediaStoreItem.width, contentUri)
117
+ return AssetInfo(
118
+ id = contentUri,
119
+ mediaType = mediaType,
120
+ creationTime = mediaStoreToAssetAdapter.transformCreationTime(mediaStoreItem.dateTaken),
121
+ modificationTime = mediaStoreToAssetAdapter.transformModificationTime(mediaStoreItem.dateModified),
122
+ duration = mediaStoreToAssetAdapter.transformDuration(mediaStoreItem.duration),
123
+ filename = mediaStoreItem.displayName
124
+ ?: throw AssetPropertyNotFoundException("Filename"),
125
+ height = height
126
+ ?: throw AssetPropertyNotFoundException("Height"),
127
+ width = width
128
+ ?: throw AssetPropertyNotFoundException("Width"),
129
+ uri = mediaStoreToAssetAdapter.transformUri(mediaStoreItem.data)
130
+ ?: throw AssetPropertyNotFoundException("Uri")
131
+ )
118
132
  }
119
133
 
120
134
  override suspend fun getMimeType(): MimeType {