expo-image-picker 13.2.1 → 13.3.0

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 CHANGED
@@ -10,6 +10,19 @@
10
10
 
11
11
  ### 💡 Others
12
12
 
13
+ ## 13.3.0 — 2022-07-16
14
+
15
+ ### 🎉 New features
16
+
17
+ - On iOS 14+ added support for selection limit and on iOS 15+ for ordered selection. ([#18142](https://github.com/expo/expo/pull/18142), [#18143](https://github.com/expo/expo/pull/18143) by [@barthap](https://github.com/barthap))
18
+ - The picker now resolves media library asset ID. ([#18236](https://github.com/expo/expo/pull/18236), [#18185](https://github.com/expo/expo/pull/18185) by [@barthap](https://github.com/barthap))
19
+ - On iOS, the picker now resolves file name and size and media library asset ID. ([#18179](https://github.com/expo/expo/pull/18179) by [@barthap](https://github.com/barthap))
20
+ - On Android added support for selecting multiple images/videos. ([#18161](https://github.com/expo/expo/pull/18161) by [@bbarthec](https://github.com/bbarthec))
21
+
22
+ ### 🐛 Bug fixes
23
+
24
+ - On Android restored support for `allowsEditing` option that was disabled when migrating to `registerForActivityResult` mechanism. ([#17963](https://github.com/expo/expo/pull/17963) by [@bbarthec](https://github.com/bbarthec))
25
+
13
26
  ## 13.2.1 — 2022-07-11
14
27
 
15
28
  _This version does not introduce any user-facing changes._
@@ -18,7 +31,7 @@ _This version does not introduce any user-facing changes._
18
31
 
19
32
  ### 🎉 New features
20
33
 
21
- - On iOS 14+ added support for selecting multiple images/videos. ([#18102](https://github.com/expo/expo/pull/18102) by [@barthap](https://github.com/barthap))
34
+ - On iOS 14+ added support for selecting multiple images/videos. ([#18102](https://github.com/expo/expo/pull/18102), [#18138](https://github.com/expo/expo/pull/18138), [#18145](https://github.com/expo/expo/pull/18145) by [@barthap](https://github.com/barthap))
22
35
 
23
36
  ### 🐛 Bug fixes
24
37
 
@@ -26,7 +39,7 @@ _This version does not introduce any user-facing changes._
26
39
 
27
40
  ### 💡 Others
28
41
 
29
- - On Android migrated to the new `registerForActivityResult` mechanism. ([#17671](https://github.com/expo/expo/pull/17671), ([#17987](https://github.com/expo/expo/pull/17987) by [@bbarthec](https://github.com/bbarthec))
42
+ - On Android migrated to the new `registerForActivityResult` mechanism. This migration disables `allowsEditing` option. ([#17671](https://github.com/expo/expo/pull/17671), ([#17987](https://github.com/expo/expo/pull/17987) by [@bbarthec](https://github.com/bbarthec))
30
43
  - Native module on Android is now written in Kotlin using [Sweet API](https://docs.expo.dev/modules/module-api). ([#17668](https://github.com/expo/expo/pull/17668) by [@bbarthec](https://github.com/bbarthec))
31
44
  - Migrated Expo modules definitions to the new naming convention. ([#17193](https://github.com/expo/expo/pull/17193) by [@tsapeta](https://github.com/tsapeta))
32
45
 
@@ -3,7 +3,7 @@ apply plugin: 'kotlin-android'
3
3
  apply plugin: 'maven-publish'
4
4
 
5
5
  group = 'host.exp.exponent'
6
- version = '13.2.1'
6
+ version = '13.3.0'
7
7
 
8
8
  buildscript {
9
9
  def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
@@ -74,7 +74,7 @@ android {
74
74
  minSdkVersion safeExtGet("minSdkVersion", 21)
75
75
  targetSdkVersion safeExtGet("targetSdkVersion", 31)
76
76
  versionCode 22
77
- versionName "13.2.1"
77
+ versionName "13.3.0"
78
78
  }
79
79
  lintOptions {
80
80
  abortOnError false
@@ -84,9 +84,11 @@ android {
84
84
  dependencies {
85
85
  implementation project(':expo-modules-core')
86
86
 
87
- implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${getKotlinVersion()}"
88
- implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.3")
89
- implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.3")
90
- implementation "com.github.CanHub:Android-Image-Cropper:1.1.1"
87
+ implementation "androidx.activity:activity-ktx:1.4.0"
88
+ implementation "androidx.appcompat:appcompat:1.4.2"
91
89
  implementation "androidx.exifinterface:exifinterface:1.3.3"
90
+ implementation "com.github.CanHub:Android-Image-Cropper:4.3.0"
91
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${getKotlinVersion()}"
92
+ implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.3"
93
+ implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.3"
92
94
  }
@@ -1,5 +1,3 @@
1
- @file:OptIn(ExperimentalContracts::class)
2
-
3
1
  package expo.modules.imagepicker
4
2
 
5
3
  import android.Manifest
@@ -10,6 +8,8 @@ import expo.modules.core.errors.ModuleNotFoundException
10
8
  import android.os.OperationCanceledException
11
9
  import expo.modules.imagepicker.contracts.CameraContract
12
10
  import expo.modules.imagepicker.contracts.CameraContractOptions
11
+ import expo.modules.imagepicker.contracts.CropImageContract
12
+ import expo.modules.imagepicker.contracts.CropImageContractOptions
13
13
  import expo.modules.imagepicker.contracts.ImageLibraryContract
14
14
  import expo.modules.imagepicker.contracts.ImageLibraryContractOptions
15
15
  import expo.modules.imagepicker.contracts.ImagePickerContractResult
@@ -24,7 +24,6 @@ import kotlinx.coroutines.Dispatchers
24
24
  import kotlinx.coroutines.launch
25
25
  import kotlinx.coroutines.suspendCancellableCoroutine
26
26
  import kotlinx.coroutines.withContext
27
- import kotlin.contracts.ExperimentalContracts
28
27
  import kotlin.coroutines.resume
29
28
  import kotlin.coroutines.resumeWithException
30
29
 
@@ -71,7 +70,7 @@ class ImagePickerModule : Module() {
71
70
  launchContract({ imageLibraryLauncher.launch(contractOptions) }, options)
72
71
  }
73
72
 
74
- AsyncFunction("getPendingResultAsync") Coroutine { _: Promise ->
73
+ AsyncFunction("getPendingResultAsync") Coroutine { ->
75
74
  val (bareResult, options) = pendingMediaPickingResult ?: return@Coroutine null
76
75
 
77
76
  pendingMediaPickingResult = null
@@ -90,6 +89,9 @@ class ImagePickerModule : Module() {
90
89
  imageLibraryLauncher = appContext.registerForActivityResult(
91
90
  ImageLibraryContract(this@ImagePickerModule),
92
91
  ) { input, result -> handleResultUponActivityDestruction(result, input.options) }
92
+ cropImageLauncher = appContext.registerForActivityResult(
93
+ CropImageContract(this@ImagePickerModule),
94
+ ) { input, result -> handleResultUponActivityDestruction(result, input.options) }
93
95
  }
94
96
  }
95
97
  }
@@ -106,6 +108,7 @@ class ImagePickerModule : Module() {
106
108
 
107
109
  private lateinit var cameraLauncher: AppContextActivityResultLauncher<CameraContractOptions, ImagePickerContractResult>
108
110
  private lateinit var imageLibraryLauncher: AppContextActivityResultLauncher<ImageLibraryContractOptions, ImagePickerContractResult>
111
+ private lateinit var cropImageLauncher: AppContextActivityResultLauncher<CropImageContractOptions, ImagePickerContractResult>
109
112
 
110
113
  /**
111
114
  * Stores result for an operation that has been interrupted by the activity destruction.
@@ -123,8 +126,18 @@ class ImagePickerModule : Module() {
123
126
  options: ImagePickerOptions,
124
127
  ): Any {
125
128
  return try {
126
- val bareResult = launchPicker(pickerLauncher)
127
- mediaHandler.readExtras(bareResult, options)
129
+ var result = launchPicker(pickerLauncher)
130
+ if (
131
+ !options.allowsMultipleSelection &&
132
+ options.allowsEditing &&
133
+ result.data.size == 1 &&
134
+ result.data[0].first == MediaType.IMAGE
135
+ ) {
136
+ result = launchPicker {
137
+ cropImageLauncher.launch(CropImageContractOptions(result.data[0].second, options))
138
+ }
139
+ }
140
+ mediaHandler.readExtras(result.data, options)
128
141
  } catch (cause: OperationCanceledException) {
129
142
  ImagePickerCancelledResponse()
130
143
  }
@@ -145,9 +158,9 @@ class ImagePickerModule : Module() {
145
158
  */
146
159
  private suspend fun launchPicker(
147
160
  pickerLauncher: suspend () -> ImagePickerContractResult,
148
- ): Pair<MediaType, Uri> = withContext(Dispatchers.Main) {
161
+ ): ImagePickerContractResult.Success = withContext(Dispatchers.Main) {
149
162
  when (val pickingResult = pickerLauncher()) {
150
- is ImagePickerContractResult.Success -> pickingResult.data
163
+ is ImagePickerContractResult.Success -> pickingResult
151
164
  is ImagePickerContractResult.Cancelled -> throw OperationCanceledException()
152
165
  }
153
166
  }
@@ -191,15 +204,10 @@ class ImagePickerModule : Module() {
191
204
  // endregion
192
205
  }
193
206
 
194
- internal enum class PickingSource {
195
- CAMERA,
196
- IMAGE_LIBRARY
197
- }
198
-
199
207
  /**
200
208
  * Simple data structure to hold the data that has to be preserved after the Activity is destroyed.
201
209
  */
202
210
  internal data class PendingMediaPickingResult(
203
- val data: Pair<MediaType, Uri>,
211
+ val data: List<Pair<MediaType, Uri>>,
204
212
  val options: ImagePickerOptions
205
213
  )
@@ -14,6 +14,9 @@ internal class ImagePickerOptions : Record, Serializable {
14
14
  @Field
15
15
  var allowsEditing: Boolean = false
16
16
 
17
+ @Field
18
+ var allowsMultipleSelection: Boolean = false
19
+
17
20
  @Field
18
21
  @FloatRange(from = 0.0, to = 1.0)
19
22
  var quality: Double = 0.2
@@ -9,32 +9,42 @@ internal class ImagePickerCancelledResponse : Record {
9
9
  val cancelled: Boolean = true
10
10
  }
11
11
 
12
- internal sealed class ImagePickerMediaResponse(
13
- @Field val type: MediaType,
14
- @Field val uri: String,
15
- @Field val width: Int,
16
- @Field val height: Int,
17
- ) : Record {
12
+ internal sealed class ImagePickerResponse : Record {
18
13
  @Field
19
14
  val cancelled: Boolean = false
20
15
 
21
- class Image(
22
- uri: String,
23
- width: Int,
24
- height: Int,
25
-
26
- @Field val base64: String?,
27
- @Field val exif: Bundle?
28
- ) : ImagePickerMediaResponse(MediaType.IMAGE, uri, width, height)
29
-
30
- class Video(
31
- uri: String,
32
- width: Int,
33
- height: Int,
34
-
35
- @Field val duration: Int,
36
- @Field val rotation: Int
37
- ) : ImagePickerMediaResponse(MediaType.VIDEO, uri, width, height)
16
+ sealed class Single(
17
+ @Field val type: MediaType,
18
+ @Field val uri: String,
19
+ @Field val width: Int,
20
+ @Field val height: Int,
21
+ @Field val assetId: String?,
22
+ ) : ImagePickerResponse() {
23
+ class Image(
24
+ uri: String,
25
+ width: Int,
26
+ height: Int,
27
+ assetId: String?,
28
+
29
+ @Field val base64: String?,
30
+ @Field val exif: Bundle?
31
+ ) : Single(MediaType.IMAGE, uri, width, height, assetId)
32
+
33
+ class Video(
34
+ uri: String,
35
+ width: Int,
36
+ height: Int,
37
+ assetId: String?,
38
+
39
+ @Field val duration: Int,
40
+ @Field val rotation: Int
41
+ ) : Single(MediaType.VIDEO, uri, width, height, assetId)
42
+ }
43
+
44
+ class Multiple(
45
+ @Field
46
+ val selected: List<Single>
47
+ ) : ImagePickerResponse()
38
48
  }
39
49
 
40
50
  enum class MediaType(val value: String) {
@@ -1,10 +1,13 @@
1
1
  package expo.modules.imagepicker
2
2
 
3
+ import android.content.ClipData
3
4
  import android.content.ContentResolver
4
5
  import android.content.Context
6
+ import android.content.Intent
5
7
  import android.graphics.Bitmap
6
8
  import android.media.MediaMetadataRetriever
7
9
  import android.net.Uri
10
+ import android.provider.DocumentsContract
8
11
  import android.util.Log
9
12
  import android.webkit.MimeTypeMap
10
13
  import androidx.core.content.FileProvider
@@ -100,6 +103,42 @@ internal fun MediaMetadataRetriever.extractInt(key: Int): Int {
100
103
  return this.extractMetadata(key)?.toInt() ?: throw FailedToExtractVideoMetadataException()
101
104
  }
102
105
 
106
+ /**
107
+ * [Iterable] implementation for [ClipData] items
108
+ */
109
+ val ClipData.items: Iterable<ClipData.Item>
110
+ get() = object : Iterable<ClipData.Item> {
111
+ override fun iterator() = object : Iterator<ClipData.Item> {
112
+ var index = 0
113
+ val count = itemCount
114
+
115
+ override fun hasNext(): Boolean = index < count
116
+
117
+ override fun next(): ClipData.Item = getItemAt(index++)
118
+ }
119
+ }
120
+
121
+ /**
122
+ * Gets all data that is associated with this [Intent].
123
+ * Original data order is preserved.
124
+ *
125
+ * Adapted from [androidx.activity.result.contract.ActivityResultContracts.GetMultipleContents.getClipDataUris]
126
+ */
127
+ internal fun Intent.getAllDataUris(): List<Uri> {
128
+ // Use a LinkedHashSet to maintain any ordering that may be present in the ClipData
129
+ val resultSet = LinkedHashSet<Uri>()
130
+
131
+ data
132
+ ?.let { resultSet.add(it) }
133
+
134
+ clipData
135
+ ?.items
136
+ ?.map { it.uri }
137
+ ?.let { resultSet.addAll(it) }
138
+
139
+ return resultSet.toList()
140
+ }
141
+
103
142
  /**
104
143
  * Copy the media file from `sourceUri` to `destinationUri`.
105
144
  *
@@ -168,3 +207,72 @@ internal suspend fun copyExifData(
168
207
  throw FailedToWriteFileException(targetFile, cause)
169
208
  }
170
209
  }
210
+
211
+ /*
212
+ Getting asset ID (and metadata) on Android is not that obvious. When getting a `content://` URI
213
+ using `ACTION_GET_CONTENT` or `ACTION_OPEN_DOCUMENT` intents, there are 3 possible ways:
214
+
215
+ 1. When the user selects a photo from **Images** section of the picker (on the left drawer)
216
+ In this case we get a URI from `com.android.providers.media.MediaDocumentsProvider`,
217
+ that inherits from `DocumentsProvider`. The URI looks like this:
218
+ ```
219
+ com.android.providers.media.documents/document/image:56
220
+ ```
221
+ In this case, the `56` is the ID we're looking for.
222
+
223
+ 2. When the user selects a photo from **Downloads** section, another content provider is used:
224
+ `DownloadStorageProvider` which is a bit different, and also differs depending on Android version:
225
+ - On API 29+ it also inherits from `com.android.providers.downloads.DocumentsProvider`
226
+ and the URI looks like this:
227
+ ```
228
+ com.android.providers.downloads.documents/document/msf:56
229
+ ```
230
+ Where "msf" is abbr. of "media store file" and 56 is our asset ID
231
+ - On API <29 it looks similar:
232
+ ```
233
+ com.android.providers.downloads.documents/document/128
234
+ ```
235
+ but the 128 is an internal ID of downloads provider, unrelated to media store asset ID.
236
+
237
+ 3. When the user selects a photo by browsing the filesystem, the URI looks like this:
238
+ ```
239
+ com.android.externalstorage.documents/document/primary:Download:filename.jpg
240
+ ```
241
+ No ID in this case
242
+ */
243
+
244
+ /**
245
+ * Checks whether this [Uri] is a `com.android.providers.media.documents` provider uri
246
+ */
247
+ internal val Uri.isMediaProviderUri
248
+ get() = this.authority == "com.android.providers.media.documents"
249
+
250
+ /**
251
+ * Checks whether this [Uri] is a `com.android.providers.downloads.documents` provider uri
252
+ */
253
+ internal val Uri.isDownloadsProviderUri
254
+ get() = this.authority == "com.android.providers.downloads.documents"
255
+
256
+ /**
257
+ * Checks whether asset represented by this [Uri] can be queried in the media store
258
+ */
259
+ internal val Uri.isMediaStoreAssetUri
260
+ get() = isMediaProviderUri || (
261
+ isDownloadsProviderUri &&
262
+ DocumentsContract
263
+ .getDocumentId(this)
264
+ .startsWith("msf:")
265
+ )
266
+
267
+ /**
268
+ * If the URI represents a media store asset, this returns its ID. Otherwise, returns `null`.
269
+ *
270
+ * See the detailed explanation above in this file (ImagePickerUtils.kt).
271
+ */
272
+ internal fun Uri.getMediaStoreAssetId(): String? {
273
+ if (isMediaStoreAssetUri) {
274
+ val rawId = DocumentsContract.getDocumentId(this)
275
+ return if (rawId.contains(':')) rawId.split(':')[1] else rawId
276
+ }
277
+ return null
278
+ }
@@ -17,17 +17,27 @@ internal class MediaHandler(
17
17
  get() = requireNotNull(appContextProvider.appContext.reactContext) { "React Application Context is null" }
18
18
 
19
19
  internal suspend fun readExtras(
20
- bareResult: Pair<MediaType, Uri>,
20
+ bareResult: List<Pair<MediaType, Uri>>,
21
21
  options: ImagePickerOptions,
22
- ): ImagePickerMediaResponse = when (bareResult.first) {
23
- MediaType.VIDEO -> handleVideo(bareResult.second)
24
- MediaType.IMAGE -> handleImage(bareResult.second, options)
22
+ ): ImagePickerResponse {
23
+ val results = bareResult.map { (mediaType, uri) ->
24
+ when (mediaType) {
25
+ MediaType.VIDEO -> handleVideo(uri)
26
+ MediaType.IMAGE -> handleImage(uri, options)
27
+ }
28
+ }
29
+
30
+ return if (results.size == 1) {
31
+ results[0]
32
+ } else {
33
+ ImagePickerResponse.Multiple(results)
34
+ }
25
35
  }
26
36
 
27
37
  private suspend fun handleImage(
28
38
  sourceUri: Uri,
29
39
  options: ImagePickerOptions,
30
- ): ImagePickerMediaResponse.Image {
40
+ ): ImagePickerResponse.Single.Image {
31
41
  val exporter: ImageExporter = if (options.quality == ImagePickerConstants.MAXIMUM_QUALITY) {
32
42
  RawImageExporter()
33
43
  } else {
@@ -43,18 +53,19 @@ internal class MediaHandler(
43
53
  val exif = options.exif.takeIf { it }
44
54
  ?.let { exportedImage.exif(context.contentResolver) }
45
55
 
46
- return ImagePickerMediaResponse.Image(
56
+ return ImagePickerResponse.Single.Image(
47
57
  uri = Uri.fromFile(outputFile).toString(),
48
58
  width = exportedImage.width,
49
59
  height = exportedImage.height,
50
60
  base64 = base64,
51
61
  exif = exif,
62
+ assetId = sourceUri.getMediaStoreAssetId()
52
63
  )
53
64
  }
54
65
 
55
66
  private suspend fun handleVideo(
56
67
  sourceUri: Uri,
57
- ): ImagePickerMediaResponse.Video {
68
+ ): ImagePickerResponse.Single.Video {
58
69
  val outputFile = createOutputFile(context.cacheDir, ".mp4")
59
70
  copyFile(sourceUri, outputFile, context.contentResolver)
60
71
  val outputUri = outputFile.toUri()
@@ -64,12 +75,13 @@ internal class MediaHandler(
64
75
  setDataSource(context, outputUri)
65
76
  }
66
77
 
67
- ImagePickerMediaResponse.Video(
78
+ ImagePickerResponse.Single.Video(
68
79
  uri = outputUri.toString(),
69
80
  width = metadataRetriever.extractInt(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH),
70
81
  height = metadataRetriever.extractInt(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT),
71
82
  duration = metadataRetriever.extractInt(MediaMetadataRetriever.METADATA_KEY_DURATION),
72
83
  rotation = metadataRetriever.extractInt(MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION),
84
+ assetId = sourceUri.getMediaStoreAssetId()
73
85
  )
74
86
  } catch (cause: FailedToExtractVideoMetadataException) {
75
87
  throw FailedToExtractVideoMetadataException(outputFile, cause)
@@ -1,6 +1,7 @@
1
1
  package expo.modules.imagepicker.contracts
2
2
 
3
3
  import android.app.Activity
4
+ import android.content.ContentResolver
4
5
  import android.content.Context
5
6
  import android.content.Intent
6
7
  import android.net.Uri
@@ -21,6 +22,11 @@ import java.io.Serializable
21
22
  internal class CameraContract(
22
23
  private val appContextProvider: AppContextProvider,
23
24
  ) : AppContextActivityResultContract<CameraContractOptions, ImagePickerContractResult> {
25
+ val contentResolver: ContentResolver
26
+ get() = requireNotNull(appContextProvider.appContext.reactContext) {
27
+ "React Application Context is null"
28
+ }.contentResolver
29
+
24
30
  override fun createIntent(context: Context, input: CameraContractOptions): Intent =
25
31
  Intent(input.options.mediaTypes.toCameraIntentAction())
26
32
  .putExtra(MediaStore.EXTRA_OUTPUT, input.uri)
@@ -34,14 +40,16 @@ internal class CameraContract(
34
40
  if (resultCode == Activity.RESULT_CANCELED) {
35
41
  ImagePickerContractResult.Cancelled()
36
42
  } else {
37
- val contentResolver = requireNotNull(appContextProvider.appContext.reactContext) { "React Application Context is null. " }.contentResolver
38
43
  val uri = input.uri
39
44
  val type = uri.toMediaType(contentResolver)
40
- ImagePickerContractResult.Success(type to uri)
45
+ ImagePickerContractResult.Success(listOf(type to uri))
41
46
  }
42
47
  }
43
48
 
44
49
  internal data class CameraContractOptions(
50
+ /**
51
+ * Destination file in a form of content-[Uri] to save results coming from camera to.
52
+ */
45
53
  val uri: Uri,
46
54
  val options: ImagePickerOptions,
47
55
  ) : Serializable
@@ -1,18 +1,12 @@
1
1
  package expo.modules.imagepicker.contracts
2
2
 
3
3
  import android.net.Uri
4
- import androidx.activity.result.contract.ActivityResultContract
5
4
  import expo.modules.imagepicker.MediaType
6
5
 
7
- /**
8
- * Alias for [androidx.activity.result.contract.ActivityResultContract] with required generic types already specified
9
- */
10
- internal typealias ImagePickerContract = ActivityResultContract<Any?, ImagePickerContractResult>
11
-
12
6
  /**
13
7
  * Data required to be returned upon successful contract completion
14
8
  */
15
9
  internal sealed class ImagePickerContractResult private constructor() {
16
10
  class Cancelled : ImagePickerContractResult()
17
- class Success(val data: Pair<MediaType, Uri>) : ImagePickerContractResult()
11
+ class Success(val data: List<Pair<MediaType, Uri>>) : ImagePickerContractResult()
18
12
  }
@@ -12,44 +12,34 @@ import com.canhub.cropper.CropImageActivity
12
12
  import com.canhub.cropper.CropImageOptions
13
13
  import expo.modules.imagepicker.ImagePickerOptions
14
14
  import expo.modules.imagepicker.MediaType
15
- import expo.modules.imagepicker.PickingSource
16
15
  import expo.modules.imagepicker.copyExifData
17
16
  import expo.modules.imagepicker.createOutputFile
18
17
  import expo.modules.imagepicker.toBitmapCompressFormat
19
18
  import expo.modules.imagepicker.toImageFileExtension
19
+ import expo.modules.kotlin.activityresult.AppContextActivityResultContract
20
20
  import expo.modules.kotlin.providers.AppContextProvider
21
21
  import kotlinx.coroutines.runBlocking
22
+ import java.io.Serializable
22
23
 
23
24
  internal class CropImageContract(
24
25
  private val appContextProvider: AppContextProvider,
25
- private val sourceUri: Uri,
26
- private val options: ImagePickerOptions,
27
- private val pickingSource: PickingSource
28
- ) : ImagePickerContract() {
29
- override fun createIntent(context: Context, input: Any?) = Intent(context, CropImageActivity::class.java).apply {
30
- // for [IMAGE_LIBRARY] we need to create a new file as up to this point we've been operating on the original media asset
31
- // for [CAMERA] we do not have to do it as it's already been created at the beginning of the picking process
32
- val needCreateNewFile = pickingSource == PickingSource.IMAGE_LIBRARY
33
- val mediaType = expo.modules.imagepicker.getType(context.contentResolver, sourceUri)
34
-
26
+ ) : AppContextActivityResultContract<CropImageContractOptions, ImagePickerContractResult> {
27
+ override fun createIntent(context: Context, input: CropImageContractOptions) = Intent(context, CropImageActivity::class.java).apply {
28
+ val mediaType = expo.modules.imagepicker.getType(context.contentResolver, input.sourceUri)
35
29
  val compressFormat = mediaType.toBitmapCompressFormat()
36
-
37
- val outputUri: Uri = if (needCreateNewFile) {
38
- createOutputFile(context.cacheDir, compressFormat.toImageFileExtension()).toUri()
39
- } else {
40
- sourceUri
41
- }
30
+ val outputUri = createOutputFile(context.cacheDir, compressFormat.toImageFileExtension()).toUri()
42
31
 
43
32
  putExtra(
44
33
  CropImage.CROP_IMAGE_EXTRA_BUNDLE,
45
34
  bundleOf(
46
- CropImage.CROP_IMAGE_EXTRA_SOURCE to sourceUri,
35
+ CropImage.CROP_IMAGE_EXTRA_SOURCE to input.sourceUri,
47
36
  CropImage.CROP_IMAGE_EXTRA_OPTIONS to CropImageOptions().apply {
48
37
  outputCompressFormat = compressFormat
49
- outputCompressQuality = (this@CropImageContract.options.quality * 100).toInt()
50
- this.outputUri = outputUri
38
+ outputCompressQuality = (input.options.quality * 100).toInt()
51
39
 
52
- this@CropImageContract.options.aspect?.let { (x, y) ->
40
+ this.customOutputUri = outputUri
41
+
42
+ input.options.aspect?.let { (x, y) ->
53
43
  aspectRatioX = x
54
44
  aspectRatioY = y
55
45
  fixAspectRatio = true
@@ -62,14 +52,19 @@ internal class CropImageContract(
62
52
  )
63
53
  }
64
54
 
65
- override fun parseResult(resultCode: Int, intent: Intent?): ImagePickerContractResult {
55
+ override fun parseResult(input: CropImageContractOptions, resultCode: Int, intent: Intent?): ImagePickerContractResult {
66
56
  val result = intent?.getParcelableExtra<CropImage.ActivityResult?>(CropImage.CROP_IMAGE_EXTRA_RESULT)
67
57
  if (resultCode == Activity.RESULT_CANCELED || result == null) {
68
58
  return ImagePickerContractResult.Cancelled()
69
59
  }
70
- val targetUri = requireNotNull(result.uri)
60
+ val targetUri = requireNotNull(result.uriContent)
71
61
  val contentResolver = requireNotNull(appContextProvider.appContext.reactContext) { "React Application Context is null" }.contentResolver
72
- runBlocking { copyExifData(sourceUri, targetUri.toFile(), contentResolver) }
73
- return ImagePickerContractResult.Success(MediaType.IMAGE to targetUri)
62
+ runBlocking { copyExifData(input.sourceUri, targetUri.toFile(), contentResolver) }
63
+ return ImagePickerContractResult.Success(listOf(MediaType.IMAGE to targetUri))
74
64
  }
75
65
  }
66
+
67
+ internal data class CropImageContractOptions(
68
+ val sourceUri: Uri,
69
+ val options: ImagePickerOptions,
70
+ ) : Serializable
@@ -1,10 +1,12 @@
1
1
  package expo.modules.imagepicker.contracts
2
2
 
3
3
  import android.app.Activity
4
+ import android.content.ContentResolver
4
5
  import android.content.Context
5
6
  import android.content.Intent
6
7
  import android.net.Uri
7
8
  import expo.modules.imagepicker.ImagePickerOptions
9
+ import expo.modules.imagepicker.getAllDataUris
8
10
  import expo.modules.imagepicker.toMediaType
9
11
  import expo.modules.kotlin.activityresult.AppContextActivityResultContract
10
12
  import expo.modules.kotlin.providers.AppContextProvider
@@ -14,25 +16,37 @@ import java.io.Serializable
14
16
  * An [androidx.activity.result.contract.ActivityResultContract] to prompt the user to pick single or multiple image(s) or/and video(s),
15
17
  * receiving a `content://` [Uri] for each piece of content.
16
18
  *
17
- * @see [androidx.activity.result.contract.ActivityResultContracts.GetContent]
19
+ * @see [androidx.activity.result.contract.ActivityResultContracts.GetContent],
20
+ * @see [androidx.activity.result.contract.ActivityResultContracts.GetMultipleContents]
18
21
  */
19
22
  internal class ImageLibraryContract(
20
23
  private val appContextProvider: AppContextProvider,
21
24
  ) : AppContextActivityResultContract<ImageLibraryContractOptions, ImagePickerContractResult> {
25
+ val contentResolver: ContentResolver
26
+ get() = requireNotNull(appContextProvider.appContext.reactContext) {
27
+ "React Application Context is null"
28
+ }.contentResolver
29
+
22
30
  override fun createIntent(context: Context, input: ImageLibraryContractOptions) =
23
31
  Intent(Intent.ACTION_GET_CONTENT)
24
32
  .addCategory(Intent.CATEGORY_OPENABLE)
25
33
  .setType(input.options.mediaTypes.toMimeType())
34
+ .apply {
35
+ if (input.options.allowsMultipleSelection) {
36
+ putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
37
+ }
38
+ }
26
39
 
27
40
  override fun parseResult(input: ImageLibraryContractOptions, resultCode: Int, intent: Intent?) =
28
41
  if (resultCode == Activity.RESULT_CANCELED) {
29
42
  ImagePickerContractResult.Cancelled()
43
+ } else if (input.options.allowsMultipleSelection) {
44
+ val uris = requireNotNull(intent).getAllDataUris()
45
+ ImagePickerContractResult.Success(uris.map { uri -> uri.toMediaType(contentResolver) to uri })
30
46
  } else {
31
47
  val uri = requireNotNull(requireNotNull(intent).data)
32
- val contentResolver = requireNotNull(appContextProvider.appContext.reactContext) { "React Application Context is null. " }.contentResolver
33
48
  val type = uri.toMediaType(contentResolver)
34
-
35
- ImagePickerContractResult.Success(type to uri)
49
+ ImagePickerContractResult.Success(listOf(type to uri))
36
50
  }
37
51
  }
38
52
 
@@ -68,7 +68,7 @@ export declare function getPendingResultAsync(): Promise<(ImagePickerResult | Im
68
68
  /**
69
69
  * Display the system UI for taking a photo with the camera. Requires `Permissions.CAMERA`.
70
70
  * On Android and iOS 10 `Permissions.CAMERA_ROLL` is also required. On mobile web, this must be
71
- * called immediately in a user interaction like a button press, otherwise the browser will bloc
71
+ * called immediately in a user interaction like a button press, otherwise the browser will block
72
72
  * the request without a warning.
73
73
  * > **Note:** Make sure that you handle `MainActivity` destruction on **Android**. See [ImagePicker.getPendingResultAsync](#imagepickergetpendingresultasync).
74
74
  * > **Notes for Web:** The system UI can only be shown after user activation (e.g. a `Button` press).
@@ -118,7 +118,7 @@ export async function getPendingResultAsync() {
118
118
  /**
119
119
  * Display the system UI for taking a photo with the camera. Requires `Permissions.CAMERA`.
120
120
  * On Android and iOS 10 `Permissions.CAMERA_ROLL` is also required. On mobile web, this must be
121
- * called immediately in a user interaction like a button press, otherwise the browser will bloc
121
+ * called immediately in a user interaction like a button press, otherwise the browser will block
122
122
  * the request without a warning.
123
123
  * > **Note:** Make sure that you handle `MainActivity` destruction on **Android**. See [ImagePicker.getPendingResultAsync](#imagepickergetpendingresultasync).
124
124
  * > **Notes for Web:** The system UI can only be shown after user activation (e.g. a `Button` press).
@@ -1 +1 @@
1
- {"version":3,"file":"ImagePicker.js","sourceRoot":"","sources":["../src/ImagePicker.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n PermissionStatus,\n PermissionExpiration,\n PermissionHookOptions,\n PermissionResponse,\n createPermissionHook,\n UnavailabilityError,\n CodedError,\n} from 'expo-modules-core';\n\nimport ExponentImagePicker from './ExponentImagePicker';\nimport {\n CameraPermissionResponse,\n CameraRollPermissionResponse,\n MediaLibraryPermissionResponse,\n ImagePickerResult,\n ImagePickerErrorResult,\n MediaTypeOptions,\n ImagePickerOptions,\n VideoExportPreset,\n ExpandImagePickerResult,\n ImageInfo,\n ImagePickerMultipleResult,\n ImagePickerCancelledResult,\n OpenFileBrowserOptions,\n UIImagePickerControllerQualityType,\n UIImagePickerPresentationStyle,\n} from './ImagePicker.types';\n\nfunction validateOptions(options: ImagePickerOptions) {\n const { aspect, quality, videoMaxDuration } = options;\n\n if (aspect != null) {\n const [x, y] = aspect;\n\n if (x <= 0 || y <= 0) {\n throw new CodedError(\n 'ERR_INVALID_ARGUMENT',\n `Invalid aspect ratio values ${x}:${y}. Provide positive numbers.`\n );\n }\n }\n\n if (quality && (quality < 0 || quality > 1)) {\n throw new CodedError(\n 'ERR_INVALID_ARGUMENT',\n `Invalid 'quality' value ${quality}. Provide a value between 0 and 1.`\n );\n }\n\n if (videoMaxDuration && videoMaxDuration < 0) {\n throw new CodedError(\n 'ERR_INVALID_ARGUMENT',\n `Invalid 'videoMaxDuration' value ${videoMaxDuration}. Provide a non-negative number.`\n );\n }\n\n return options;\n}\n\n// @needsAudit\n/**\n * Checks user's permissions for accessing camera.\n * @return A promise that fulfills with an object of type [CameraPermissionResponse](#camerarollpermissionresponse).\n */\nexport async function getCameraPermissionsAsync(): Promise<CameraPermissionResponse> {\n return ExponentImagePicker.getCameraPermissionsAsync();\n}\n\n/**\n * @deprecated Use `getMediaLibraryPermissionsAsync()` instead.\n */\nexport async function getCameraRollPermissionsAsync(): Promise<MediaLibraryPermissionResponse> {\n console.warn(\n 'ImagePicker.getCameraRollPermissionsAsync() is deprecated in favour of ImagePicker.getMediaLibraryPermissionsAsync()'\n );\n return getMediaLibraryPermissionsAsync();\n}\n\n// @needsAudit\n/**\n * Checks user's permissions for accessing photos.\n * @param writeOnly Whether to request write or read and write permissions. Defaults to `false`\n * @return A promise that fulfills with an object of type [MediaLibraryPermissionResponse](#medialibrarypermissionresponse).\n */\nexport async function getMediaLibraryPermissionsAsync(\n writeOnly: boolean = false\n): Promise<MediaLibraryPermissionResponse> {\n return ExponentImagePicker.getMediaLibraryPermissionsAsync(writeOnly);\n}\n\n// @needsAudit\n/**\n * Asks the user to grant permissions for accessing camera. This does nothing on web because the\n * browser camera is not used.\n * @return A promise that fulfills with an object of type [CameraPermissionResponse](#camerarollpermissionresponse).\n */\nexport async function requestCameraPermissionsAsync(): Promise<CameraPermissionResponse> {\n return ExponentImagePicker.requestCameraPermissionsAsync();\n}\n\n/**\n * @deprecated Use `requestMediaLibraryPermissionsAsync()` instead.\n */\nexport async function requestCameraRollPermissionsAsync(): Promise<MediaLibraryPermissionResponse> {\n console.warn(\n 'ImagePicker.requestCameraRollPermissionsAsync() is deprecated in favour of ImagePicker.requestMediaLibraryPermissionsAsync()'\n );\n return requestMediaLibraryPermissionsAsync();\n}\n\n// @needsAudit\n/**\n * Asks the user to grant permissions for accessing user's photo. This method does nothing on web.\n * @param writeOnly Whether to request write or read and write permissions. Defaults to `false`\n * @return A promise that fulfills with an object of type [MediaLibraryPermissionResponse](#medialibrarypermissionresponse).\n */\nexport async function requestMediaLibraryPermissionsAsync(\n writeOnly: boolean = false\n): Promise<MediaLibraryPermissionResponse> {\n const imagePickerMethod = ExponentImagePicker.requestMediaLibraryPermissionsAsync;\n return imagePickerMethod(writeOnly);\n}\n\n// @needsAudit\n/**\n * Check or request permissions to access the media library.\n * This uses both `requestMediaLibraryPermissionsAsync` and `getMediaLibraryPermissionsAsync` to interact with the permissions.\n *\n * @example\n * ```ts\n * const [status, requestPermission] = ImagePicker.useMediaLibraryPermissions();\n * ```\n */\nexport const useMediaLibraryPermissions = createPermissionHook<\n MediaLibraryPermissionResponse,\n { writeOnly?: boolean }\n>({\n // TODO(cedric): permission requesters should have an options param or a different requester\n getMethod: (options) => getMediaLibraryPermissionsAsync(options?.writeOnly),\n requestMethod: (options) => requestMediaLibraryPermissionsAsync(options?.writeOnly),\n});\n\n// @needsAudit\n/**\n * Check or request permissions to access the camera.\n * This uses both `requestCameraPermissionsAsync` and `getCameraPermissionsAsync` to interact with the permissions.\n *\n * @example\n * ```ts\n * const [status, requestPermission] = ImagePicker.useCameraPermissions();\n * ```\n */\nexport const useCameraPermissions = createPermissionHook({\n getMethod: getCameraPermissionsAsync,\n requestMethod: requestCameraPermissionsAsync,\n});\n\n// @needsAudit\n/**\n * Android system sometimes kills the `MainActivity` after the `ImagePicker` finishes. When this\n * happens, we lost the data selected from the `ImagePicker`. However, you can retrieve the lost\n * data by calling `getPendingResultAsync`. You can test this functionality by turning on\n * `Don't keep activities` in the developer options.\n * @return\n * - **On Android:** a promise that resolves to an array of objects of exactly same type as in\n * `ImagePicker.launchImageLibraryAsync` or `ImagePicker.launchCameraAsync` if the `ImagePicker`\n * finished successfully. Otherwise, to the array of [`ImagePickerErrorResult`](#imagepickerimagepickererrorresult).\n * - **On other platforms:** an empty array.\n */\nexport async function getPendingResultAsync(): Promise<\n (ImagePickerResult | ImagePickerErrorResult)[]\n> {\n if (ExponentImagePicker.getPendingResultAsync) {\n return ExponentImagePicker.getPendingResultAsync();\n }\n return [];\n}\n\n// @needsAudit\n/**\n * Display the system UI for taking a photo with the camera. Requires `Permissions.CAMERA`.\n * On Android and iOS 10 `Permissions.CAMERA_ROLL` is also required. On mobile web, this must be\n * called immediately in a user interaction like a button press, otherwise the browser will bloc\n * the request without a warning.\n * > **Note:** Make sure that you handle `MainActivity` destruction on **Android**. See [ImagePicker.getPendingResultAsync](#imagepickergetpendingresultasync).\n * > **Notes for Web:** The system UI can only be shown after user activation (e.g. a `Button` press).\n * Therefore, calling `launchCameraAsync` in `componentDidMount`, for example, will **not** work as\n * intended. The `cancelled` event will not be returned in the browser due to platform restrictions\n * and inconsistencies across browsers.\n * @param options An `ImagePickerOptions` object.\n * @return If the user cancelled the action, the method returns `{ cancelled: true }`. Otherwise,\n * this method returns information about the selected media item. When the chosen item is an image,\n * this method returns `{ cancelled: false, type: 'image', uri, width, height, exif, base64 }`;\n * when the item is a video, this method returns `{ cancelled: false, type: 'video', uri, width, height, duration }`.\n */\nexport async function launchCameraAsync(\n options: ImagePickerOptions = {}\n): Promise<ImagePickerResult> {\n if (!ExponentImagePicker.launchCameraAsync) {\n throw new UnavailabilityError('ImagePicker', 'launchCameraAsync');\n }\n return await ExponentImagePicker.launchCameraAsync(validateOptions(options));\n}\n\n// @needsAudit\n/**\n * Display the system UI for choosing an image or a video from the phone's library.\n * Requires `Permissions.MEDIA_LIBRARY` on iOS 10 only. On mobile web, this must be called\n * immediately in a user interaction like a button press, otherwise the browser will block the\n * request without a warning.\n * **Animated GIFs support** If the selected image is an animated GIF, the result image will be an\n * animated GIF too if and only if `quality` is set to `undefined` and `allowsEditing` is set to `false`.\n * Otherwise compression and/or cropper will pick the first frame of the GIF and return it as the\n * result (on Android the result will be a PNG, on iOS — GIF).\n * > **Notes for Web:** The system UI can only be shown after user activation (e.g. a `Button` press).\n * Therefore, calling `launchImageLibraryAsync` in `componentDidMount`, for example, will **not**\n * work as intended. The `cancelled` event will not be returned in the browser due to platform\n * restrictions and inconsistencies across browsers.\n * @param options An object extended by [`ImagePickerOptions`](#imagepickeroptions).\n * @return If the user cancelled the action, the method returns `{ cancelled: true }`. Otherwise,\n * this method returns information about the selected media item. When the chosen item is an image,\n * this method returns `{ cancelled: false, type: 'image', uri, width, height, exif, base64 }`;\n * when the item is a video, this method returns `{ cancelled: false, type: 'video', uri, width, height, duration }`.\n */\nexport async function launchImageLibraryAsync<T extends ImagePickerOptions>(\n options?: T\n): Promise<ExpandImagePickerResult<T>> {\n if (!ExponentImagePicker.launchImageLibraryAsync) {\n throw new UnavailabilityError('ImagePicker', 'launchImageLibraryAsync');\n }\n if (options?.allowsEditing && options.allowsMultipleSelection) {\n console.warn(\n '[expo-image-picker] `allowsEditing` is not supported when `allowsMultipleSelection` is enabled and will be ignored.' +\n \"Disable either 'allowsEditing' or 'allowsMultipleSelection' in 'launchImageLibraryAsync' \" +\n 'to fix this warning.'\n );\n }\n return await ExponentImagePicker.launchImageLibraryAsync(options ?? {});\n}\n\nexport {\n MediaTypeOptions,\n ImagePickerOptions,\n ImagePickerResult,\n ImagePickerErrorResult,\n VideoExportPreset,\n CameraPermissionResponse,\n CameraRollPermissionResponse,\n MediaLibraryPermissionResponse,\n PermissionStatus,\n PermissionExpiration,\n PermissionHookOptions,\n PermissionResponse,\n ImageInfo,\n ImagePickerMultipleResult,\n ImagePickerCancelledResult,\n OpenFileBrowserOptions,\n ExpandImagePickerResult,\n UIImagePickerControllerQualityType,\n UIImagePickerPresentationStyle,\n};\n"]}
1
+ {"version":3,"file":"ImagePicker.js","sourceRoot":"","sources":["../src/ImagePicker.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n PermissionStatus,\n PermissionExpiration,\n PermissionHookOptions,\n PermissionResponse,\n createPermissionHook,\n UnavailabilityError,\n CodedError,\n} from 'expo-modules-core';\n\nimport ExponentImagePicker from './ExponentImagePicker';\nimport {\n CameraPermissionResponse,\n CameraRollPermissionResponse,\n MediaLibraryPermissionResponse,\n ImagePickerResult,\n ImagePickerErrorResult,\n MediaTypeOptions,\n ImagePickerOptions,\n VideoExportPreset,\n ExpandImagePickerResult,\n ImageInfo,\n ImagePickerMultipleResult,\n ImagePickerCancelledResult,\n OpenFileBrowserOptions,\n UIImagePickerControllerQualityType,\n UIImagePickerPresentationStyle,\n} from './ImagePicker.types';\n\nfunction validateOptions(options: ImagePickerOptions) {\n const { aspect, quality, videoMaxDuration } = options;\n\n if (aspect != null) {\n const [x, y] = aspect;\n\n if (x <= 0 || y <= 0) {\n throw new CodedError(\n 'ERR_INVALID_ARGUMENT',\n `Invalid aspect ratio values ${x}:${y}. Provide positive numbers.`\n );\n }\n }\n\n if (quality && (quality < 0 || quality > 1)) {\n throw new CodedError(\n 'ERR_INVALID_ARGUMENT',\n `Invalid 'quality' value ${quality}. Provide a value between 0 and 1.`\n );\n }\n\n if (videoMaxDuration && videoMaxDuration < 0) {\n throw new CodedError(\n 'ERR_INVALID_ARGUMENT',\n `Invalid 'videoMaxDuration' value ${videoMaxDuration}. Provide a non-negative number.`\n );\n }\n\n return options;\n}\n\n// @needsAudit\n/**\n * Checks user's permissions for accessing camera.\n * @return A promise that fulfills with an object of type [CameraPermissionResponse](#camerarollpermissionresponse).\n */\nexport async function getCameraPermissionsAsync(): Promise<CameraPermissionResponse> {\n return ExponentImagePicker.getCameraPermissionsAsync();\n}\n\n/**\n * @deprecated Use `getMediaLibraryPermissionsAsync()` instead.\n */\nexport async function getCameraRollPermissionsAsync(): Promise<MediaLibraryPermissionResponse> {\n console.warn(\n 'ImagePicker.getCameraRollPermissionsAsync() is deprecated in favour of ImagePicker.getMediaLibraryPermissionsAsync()'\n );\n return getMediaLibraryPermissionsAsync();\n}\n\n// @needsAudit\n/**\n * Checks user's permissions for accessing photos.\n * @param writeOnly Whether to request write or read and write permissions. Defaults to `false`\n * @return A promise that fulfills with an object of type [MediaLibraryPermissionResponse](#medialibrarypermissionresponse).\n */\nexport async function getMediaLibraryPermissionsAsync(\n writeOnly: boolean = false\n): Promise<MediaLibraryPermissionResponse> {\n return ExponentImagePicker.getMediaLibraryPermissionsAsync(writeOnly);\n}\n\n// @needsAudit\n/**\n * Asks the user to grant permissions for accessing camera. This does nothing on web because the\n * browser camera is not used.\n * @return A promise that fulfills with an object of type [CameraPermissionResponse](#camerarollpermissionresponse).\n */\nexport async function requestCameraPermissionsAsync(): Promise<CameraPermissionResponse> {\n return ExponentImagePicker.requestCameraPermissionsAsync();\n}\n\n/**\n * @deprecated Use `requestMediaLibraryPermissionsAsync()` instead.\n */\nexport async function requestCameraRollPermissionsAsync(): Promise<MediaLibraryPermissionResponse> {\n console.warn(\n 'ImagePicker.requestCameraRollPermissionsAsync() is deprecated in favour of ImagePicker.requestMediaLibraryPermissionsAsync()'\n );\n return requestMediaLibraryPermissionsAsync();\n}\n\n// @needsAudit\n/**\n * Asks the user to grant permissions for accessing user's photo. This method does nothing on web.\n * @param writeOnly Whether to request write or read and write permissions. Defaults to `false`\n * @return A promise that fulfills with an object of type [MediaLibraryPermissionResponse](#medialibrarypermissionresponse).\n */\nexport async function requestMediaLibraryPermissionsAsync(\n writeOnly: boolean = false\n): Promise<MediaLibraryPermissionResponse> {\n const imagePickerMethod = ExponentImagePicker.requestMediaLibraryPermissionsAsync;\n return imagePickerMethod(writeOnly);\n}\n\n// @needsAudit\n/**\n * Check or request permissions to access the media library.\n * This uses both `requestMediaLibraryPermissionsAsync` and `getMediaLibraryPermissionsAsync` to interact with the permissions.\n *\n * @example\n * ```ts\n * const [status, requestPermission] = ImagePicker.useMediaLibraryPermissions();\n * ```\n */\nexport const useMediaLibraryPermissions = createPermissionHook<\n MediaLibraryPermissionResponse,\n { writeOnly?: boolean }\n>({\n // TODO(cedric): permission requesters should have an options param or a different requester\n getMethod: (options) => getMediaLibraryPermissionsAsync(options?.writeOnly),\n requestMethod: (options) => requestMediaLibraryPermissionsAsync(options?.writeOnly),\n});\n\n// @needsAudit\n/**\n * Check or request permissions to access the camera.\n * This uses both `requestCameraPermissionsAsync` and `getCameraPermissionsAsync` to interact with the permissions.\n *\n * @example\n * ```ts\n * const [status, requestPermission] = ImagePicker.useCameraPermissions();\n * ```\n */\nexport const useCameraPermissions = createPermissionHook({\n getMethod: getCameraPermissionsAsync,\n requestMethod: requestCameraPermissionsAsync,\n});\n\n// @needsAudit\n/**\n * Android system sometimes kills the `MainActivity` after the `ImagePicker` finishes. When this\n * happens, we lost the data selected from the `ImagePicker`. However, you can retrieve the lost\n * data by calling `getPendingResultAsync`. You can test this functionality by turning on\n * `Don't keep activities` in the developer options.\n * @return\n * - **On Android:** a promise that resolves to an array of objects of exactly same type as in\n * `ImagePicker.launchImageLibraryAsync` or `ImagePicker.launchCameraAsync` if the `ImagePicker`\n * finished successfully. Otherwise, to the array of [`ImagePickerErrorResult`](#imagepickerimagepickererrorresult).\n * - **On other platforms:** an empty array.\n */\nexport async function getPendingResultAsync(): Promise<\n (ImagePickerResult | ImagePickerErrorResult)[]\n> {\n if (ExponentImagePicker.getPendingResultAsync) {\n return ExponentImagePicker.getPendingResultAsync();\n }\n return [];\n}\n\n// @needsAudit\n/**\n * Display the system UI for taking a photo with the camera. Requires `Permissions.CAMERA`.\n * On Android and iOS 10 `Permissions.CAMERA_ROLL` is also required. On mobile web, this must be\n * called immediately in a user interaction like a button press, otherwise the browser will block\n * the request without a warning.\n * > **Note:** Make sure that you handle `MainActivity` destruction on **Android**. See [ImagePicker.getPendingResultAsync](#imagepickergetpendingresultasync).\n * > **Notes for Web:** The system UI can only be shown after user activation (e.g. a `Button` press).\n * Therefore, calling `launchCameraAsync` in `componentDidMount`, for example, will **not** work as\n * intended. The `cancelled` event will not be returned in the browser due to platform restrictions\n * and inconsistencies across browsers.\n * @param options An `ImagePickerOptions` object.\n * @return If the user cancelled the action, the method returns `{ cancelled: true }`. Otherwise,\n * this method returns information about the selected media item. When the chosen item is an image,\n * this method returns `{ cancelled: false, type: 'image', uri, width, height, exif, base64 }`;\n * when the item is a video, this method returns `{ cancelled: false, type: 'video', uri, width, height, duration }`.\n */\nexport async function launchCameraAsync(\n options: ImagePickerOptions = {}\n): Promise<ImagePickerResult> {\n if (!ExponentImagePicker.launchCameraAsync) {\n throw new UnavailabilityError('ImagePicker', 'launchCameraAsync');\n }\n return await ExponentImagePicker.launchCameraAsync(validateOptions(options));\n}\n\n// @needsAudit\n/**\n * Display the system UI for choosing an image or a video from the phone's library.\n * Requires `Permissions.MEDIA_LIBRARY` on iOS 10 only. On mobile web, this must be called\n * immediately in a user interaction like a button press, otherwise the browser will block the\n * request without a warning.\n * **Animated GIFs support** If the selected image is an animated GIF, the result image will be an\n * animated GIF too if and only if `quality` is set to `undefined` and `allowsEditing` is set to `false`.\n * Otherwise compression and/or cropper will pick the first frame of the GIF and return it as the\n * result (on Android the result will be a PNG, on iOS — GIF).\n * > **Notes for Web:** The system UI can only be shown after user activation (e.g. a `Button` press).\n * Therefore, calling `launchImageLibraryAsync` in `componentDidMount`, for example, will **not**\n * work as intended. The `cancelled` event will not be returned in the browser due to platform\n * restrictions and inconsistencies across browsers.\n * @param options An object extended by [`ImagePickerOptions`](#imagepickeroptions).\n * @return If the user cancelled the action, the method returns `{ cancelled: true }`. Otherwise,\n * this method returns information about the selected media item. When the chosen item is an image,\n * this method returns `{ cancelled: false, type: 'image', uri, width, height, exif, base64 }`;\n * when the item is a video, this method returns `{ cancelled: false, type: 'video', uri, width, height, duration }`.\n */\nexport async function launchImageLibraryAsync<T extends ImagePickerOptions>(\n options?: T\n): Promise<ExpandImagePickerResult<T>> {\n if (!ExponentImagePicker.launchImageLibraryAsync) {\n throw new UnavailabilityError('ImagePicker', 'launchImageLibraryAsync');\n }\n if (options?.allowsEditing && options.allowsMultipleSelection) {\n console.warn(\n '[expo-image-picker] `allowsEditing` is not supported when `allowsMultipleSelection` is enabled and will be ignored.' +\n \"Disable either 'allowsEditing' or 'allowsMultipleSelection' in 'launchImageLibraryAsync' \" +\n 'to fix this warning.'\n );\n }\n return await ExponentImagePicker.launchImageLibraryAsync(options ?? {});\n}\n\nexport {\n MediaTypeOptions,\n ImagePickerOptions,\n ImagePickerResult,\n ImagePickerErrorResult,\n VideoExportPreset,\n CameraPermissionResponse,\n CameraRollPermissionResponse,\n MediaLibraryPermissionResponse,\n PermissionStatus,\n PermissionExpiration,\n PermissionHookOptions,\n PermissionResponse,\n ImageInfo,\n ImagePickerMultipleResult,\n ImagePickerCancelledResult,\n OpenFileBrowserOptions,\n ExpandImagePickerResult,\n UIImagePickerControllerQualityType,\n UIImagePickerPresentationStyle,\n};\n"]}
@@ -205,6 +205,17 @@ export declare type ImageInfo = {
205
205
  * an image) and `width` and `height` specify the dimensions of the media.
206
206
  */
207
207
  uri: string;
208
+ /**
209
+ * The unique ID that represents the picked image or video, if picked from the library. It can be used
210
+ * by [expo-media-library](./media-library) to manage the picked asset.
211
+ *
212
+ * > This might be `null` when the ID is unavailable or the user gave limited permission to access the media library.
213
+ * > On Android, the ID is unavailable when the user selects a photo by directly browsing file system.
214
+ *
215
+ * @platform ios
216
+ * @platform android
217
+ */
218
+ assetId?: string | null;
208
219
  /**
209
220
  * Width of the image or video.
210
221
  */
@@ -1 +1 @@
1
- {"version":3,"file":"ImagePicker.types.d.ts","sourceRoot":"","sources":["../src/ImagePicker.types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ImagePicker.types.d.ts","sourceRoot":"","sources":["../src/ImagePicker.types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAGvD;;GAEG;AACH,oBAAY,wBAAwB,GAAG,kBAAkB,CAAC;AAG1D;;GAEG;AACH,oBAAY,8BAA8B,GAAG,kBAAkB,GAAG;IAChE;;OAEG;IACH,gBAAgB,CAAC,EAAE,KAAK,GAAG,SAAS,GAAG,MAAM,CAAC;CAC/C,CAAC;AAGF;;;GAGG;AACH,oBAAY,4BAA4B,GAAG,8BAA8B,CAAC;AAG1E,oBAAY,gBAAgB;IAC1B;;OAEG;IACH,GAAG,QAAQ;IACX;;OAEG;IACH,MAAM,WAAW;IACjB;;OAEG;IACH,MAAM,WAAW;CAClB;AAGD,oBAAY,iBAAiB;IAC3B;;;;OAIG;IACH,WAAW,IAAI;IACf;;;;OAIG;IACH,UAAU,IAAI;IACd;;;;OAIG;IACH,aAAa,IAAI;IACjB;;;;OAIG;IACH,cAAc,IAAI;IAClB;;;;OAIG;IACH,YAAY,IAAI;IAChB;;;;OAIG;IACH,YAAY,IAAI;IAChB;;;;OAIG;IACH,aAAa,IAAI;IACjB;;;;OAIG;IACH,cAAc,IAAI;IAClB;;;;OAIG;IACH,cAAc,IAAI;IAClB;;;;OAIG;IACH,cAAc,IAAI;IAClB;;;;OAIG;IACH,cAAc,KAAK;CACpB;AAGD,oBAAY,kCAAkC;IAC5C;;OAEG;IACH,IAAI,IAAI;IACR;;OAEG;IACH,MAAM,IAAI;IACV;;OAEG;IACH,GAAG,IAAI;IACP;;OAEG;IACH,UAAU,IAAI;IACd;;OAEG;IACH,cAAc,IAAI;IAClB;;OAEG;IACH,aAAa,IAAI;CAClB;AAED;;;;GAIG;AACH,oBAAY,8BAA8B;IACxC;;OAEG;IACH,WAAW,eAAe;IAC1B;;OAEG;IACH,UAAU,cAAc;IACxB;;OAEG;IACH,UAAU,cAAc;IACxB;;OAEG;IACH,eAAe,mBAAmB;IAClC;;OAEG;IACH,gBAAgB,mBAAmB;IACnC;;OAEG;IACH,oBAAoB,uBAAuB;IAC3C;;OAEG;IACH,OAAO,YAAY;IACnB;;;;;OAKG;IACH,SAAS,cAAc;IACvB;;OAEG;IACH,UAAU,eAAe;IACzB;;OAEG;IACH,SAAS,cAAc;IACvB;;OAEG;IACH,SAAS,cAAc;IACvB;;OAEG;IACH,cAAc,mBAAmB;IACjC;;OAEG;IACH,cAAc,mBAAmB;IACjC;;OAEG;IACH,kBAAkB,uBAAuB;IACzC;;OAEG;IACH,OAAO,YAAY;IACnB;;OAEG;IACH,SAAS,cAAc;CACxB;AAGD,oBAAY,SAAS,GAAG;IACtB;;;OAGG;IACH,GAAG,EAAE,MAAM,CAAC;IACZ;;;;;;;;;OASG;IACH,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IACd;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;IACf;;OAEG;IACH,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC;IACzB;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC3B;;;;;;;;;;OAUG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,SAAS,EAAE,OAAO,CAAC;CACpB,CAAC;AAGF,oBAAY,sBAAsB,GAAG;IACnC;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IACb;;OAEG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAGF;;GAEG;AACH,oBAAY,0BAA0B,GAAG;IAAE,SAAS,EAAE,IAAI,CAAA;CAAE,CAAC;AAG7D,oBAAY,iBAAiB,GAAG,0BAA0B,GAAG,SAAS,CAAC;AAGvE,oBAAY,yBAAyB,GACjC,0BAA0B,GAC1B;IAAE,SAAS,EAAE,KAAK,CAAC;IAAC,QAAQ,EAAE,SAAS,EAAE,CAAA;CAAE,CAAC;AAGhD,oBAAY,kBAAkB,GAAG;IAC/B;;;;;;;;;;OAUG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB;;;;OAIG;IACH,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1B;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,UAAU,CAAC,EAAE,gBAAgB,CAAC;IAC9B;;;OAGG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC;IACf;;OAEG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;;;;OAMG;IACH,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;IACtC;;;;OAIG;IACH,YAAY,CAAC,EAAE,kCAAkC,CAAC;IAClD;;;;;;;;;OASG;IACH,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC;;;;;;OAMG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;;;;;;;OASG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B;;;;;;;OAOG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;;;;OAKG;IACH,iBAAiB,CAAC,EAAE,8BAA8B,CAAC;CACpD,CAAC;AAGF,oBAAY,sBAAsB,GAAG;IACnC;;;OAGG;IACH,UAAU,EAAE,gBAAgB,CAAC;IAE7B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;OAGG;IACH,uBAAuB,EAAE,OAAO,CAAC;IACjC;;OAEG;IACH,MAAM,EAAE,OAAO,CAAC;CACjB,CAAC;AAGF,oBAAY,uBAAuB,CAAC,CAAC,SAAS,kBAAkB,GAAG,sBAAsB,IACvF,CAAC,SAAS;IACR,uBAAuB,EAAE,IAAI,CAAC;CAC/B,GACG,yBAAyB,GACzB,iBAAiB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"ImagePicker.types.js","sourceRoot":"","sources":["../src/ImagePicker.types.ts"],"names":[],"mappings":"AA0BA,cAAc;AACd,MAAM,CAAN,IAAY,gBAaX;AAbD,WAAY,gBAAgB;IAC1B;;OAEG;IACH,+BAAW,CAAA;IACX;;OAEG;IACH,qCAAiB,CAAA;IACjB;;OAEG;IACH,qCAAiB,CAAA;AACnB,CAAC,EAbW,gBAAgB,KAAhB,gBAAgB,QAa3B;AAED,cAAc;AACd,MAAM,CAAN,IAAY,iBAmEX;AAnED,WAAY,iBAAiB;IAC3B;;;;OAIG;IACH,uEAAe,CAAA;IACf;;;;OAIG;IACH,qEAAc,CAAA;IACd;;;;OAIG;IACH,2EAAiB,CAAA;IACjB;;;;OAIG;IACH,6EAAkB,CAAA;IAClB;;;;OAIG;IACH,yEAAgB,CAAA;IAChB;;;;OAIG;IACH,yEAAgB,CAAA;IAChB;;;;OAIG;IACH,2EAAiB,CAAA;IACjB;;;;OAIG;IACH,6EAAkB,CAAA;IAClB;;;;OAIG;IACH,6EAAkB,CAAA;IAClB;;;;OAIG;IACH,6EAAkB,CAAA;IAClB;;;;OAIG;IACH,8EAAmB,CAAA;AACrB,CAAC,EAnEW,iBAAiB,KAAjB,iBAAiB,QAmE5B;AAED,cAAc;AACd,MAAM,CAAN,IAAY,kCAyBX;AAzBD,WAAY,kCAAkC;IAC5C;;OAEG;IACH,2FAAQ,CAAA;IACR;;OAEG;IACH,+FAAU,CAAA;IACV;;OAEG;IACH,yFAAO,CAAA;IACP;;OAEG;IACH,uGAAc,CAAA;IACd;;OAEG;IACH,+GAAkB,CAAA;IAClB;;OAEG;IACH,6GAAiB,CAAA;AACnB,CAAC,EAzBW,kCAAkC,KAAlC,kCAAkC,QAyB7C;AAED;;;;GAIG;AACH,MAAM,CAAN,IAAY,8BAoEX;AApED,WAAY,8BAA8B;IACxC;;OAEG;IACH,4DAA0B,CAAA;IAC1B;;OAEG;IACH,0DAAwB,CAAA;IACxB;;OAEG;IACH,0DAAwB,CAAA;IACxB;;OAEG;IACH,oEAAkC,CAAA;IAClC;;OAEG;IACH,qEAAmC,CAAA;IACnC;;OAEG;IACH,6EAA2C,CAAA;IAC3C;;OAEG;IACH,qDAAmB,CAAA;IACnB;;;;;OAKG;IACH,yDAAuB,CAAA;IACvB;;OAEG;IACH,2DAAyB,CAAA;IACzB;;OAEG;IACH,yDAAuB,CAAA;IACvB;;OAEG;IACH,yDAAuB,CAAA;IACvB;;OAEG;IACH,mEAAiC,CAAA;IACjC;;OAEG;IACH,mEAAiC,CAAA;IACjC;;OAEG;IACH,2EAAyC,CAAA;IACzC;;OAEG;IACH,qDAAmB,CAAA;IACnB;;OAEG;IACH,yDAAuB,CAAA;AACzB,CAAC,EApEW,8BAA8B,KAA9B,8BAA8B,QAoEzC","sourcesContent":["import { PermissionResponse } from 'expo-modules-core';\n\n// @needsAudit\n/**\n * Alias for `PermissionResponse` type exported by `expo-modules-core`.\n */\nexport type CameraPermissionResponse = PermissionResponse;\n\n// @needsAudit\n/**\n * Extends `PermissionResponse` type exported by `expo-modules-core` and contains additional iOS-specific field.\n */\nexport type MediaLibraryPermissionResponse = PermissionResponse & {\n /**\n * @platform ios\n */\n accessPrivileges?: 'all' | 'limited' | 'none';\n};\n\n// @needsAudit\n/**\n * An alias for the `MediaLibraryPermissionResponse` object.\n * @deprecated Use `ImagePicker.MediaLibraryPermissionResponse` instead.\n */\nexport type CameraRollPermissionResponse = MediaLibraryPermissionResponse;\n\n// @needsAudit\nexport enum MediaTypeOptions {\n /**\n * Images and videos.\n */\n All = 'All',\n /**\n * Only videos.\n */\n Videos = 'Videos',\n /**\n * Only images.\n */\n Images = 'Images',\n}\n\n// @needsAudit\nexport enum VideoExportPreset {\n /**\n * Resolution: __Unchanged__ •\n * Video compression: __None__ •\n * Audio compression: __None__\n */\n Passthrough = 0,\n /**\n * Resolution: __Depends on the device__ •\n * Video compression: __H.264__ •\n * Audio compression: __AAC__\n */\n LowQuality = 1,\n /**\n * Resolution: __Depends on the device__ •\n * Video compression: __H.264__ •\n * Audio compression: __AAC__\n */\n MediumQuality = 2,\n /**\n * Resolution: __Depends on the device__ •\n * Video compression: __H.264__ •\n * Audio compression: __AAC__\n */\n HighestQuality = 3,\n /**\n * Resolution: __640 × 480__ •\n * Video compression: __H.264__ •\n * Audio compression: __AAC__\n */\n H264_640x480 = 4,\n /**\n * Resolution: __960 × 540__ •\n * Video compression: __H.264__ •\n * Audio compression: __AAC__\n */\n H264_960x540 = 5,\n /**\n * Resolution: __1280 × 720__ •\n * Video compression: __H.264__ •\n * Audio compression: __AAC__\n */\n H264_1280x720 = 6,\n /**\n * Resolution: __1920 × 1080__ •\n * Video compression: __H.264__ •\n * Audio compression: __AAC__\n */\n H264_1920x1080 = 7,\n /**\n * Resolution: __3840 × 2160__ •\n * Video compression: __H.264__ •\n * Audio compression: __AAC__\n */\n H264_3840x2160 = 8,\n /**\n * Resolution: __1920 × 1080__ •\n * Video compression: __HEVC__ •\n * Audio compression: __AAC__\n */\n HEVC_1920x1080 = 9,\n /**\n * Resolution: __3840 × 2160__ •\n * Video compression: __HEVC__ •\n * Audio compression: __AAC__\n */\n HEVC_3840x2160 = 10,\n}\n\n// @needsAudit\nexport enum UIImagePickerControllerQualityType {\n /**\n * Highest available resolution.\n */\n High = 0,\n /**\n * Depends on the device.\n */\n Medium = 1,\n /**\n * Depends on the device.\n */\n Low = 2,\n /**\n * 640 × 480\n */\n VGA640x480 = 3,\n /**\n * 1280 × 720\n */\n IFrame1280x720 = 4,\n /**\n * 960 × 540\n */\n IFrame960x540 = 5,\n}\n\n/**\n * Picker presentation style. Its values are directly mapped to the [`UIModalPresentationStyle`](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621355-modalpresentationstyle).\n *\n * @platform ios\n */\nexport enum UIImagePickerPresentationStyle {\n /**\n * A presentation style in which the presented picker covers the screen.\n */\n FULL_SCREEN = 'fullScreen',\n /**\n * A presentation style that partially covers the underlying content.\n */\n PAGE_SHEET = 'pageSheet',\n /**\n * A presentation style that displays the picker centered in the screen.\n */\n FORM_SHEET = 'formSheet',\n /**\n * A presentation style where the picker is displayed over the app's content.\n */\n CURRENT_CONTEXT = 'currentContext',\n /**\n * A presentation style in which the picker view covers the screen.\n */\n OVER_FULL_SCREEN = 'overFullScreen',\n /**\n * A presentation style where the picker is displayed over the app's content.\n */\n OVER_CURRENT_CONTEXT = 'overCurrentContext',\n /**\n * A presentation style where the picker is displayed in a popover view.\n */\n POPOVER = 'popover',\n /**\n * The default presentation style chosen by the system.\n * On older iOS versions, falls back to `WebBrowserPresentationStyle.FullScreen`.\n *\n * @platform ios 13+\n */\n AUTOMATIC = 'automatic',\n /**\n * @deprecated Use `UIImagePickerPresentationStyle.FULL_SCREEN` instead.\n */\n FullScreen = 'fullScreen',\n /**\n * @deprecated Use `UIImagePickerPresentationStyle.PAGE_SHEET` instead.\n */\n PageSheet = 'pageSheet',\n /**\n * @deprecated Use `UIImagePickerPresentationStyle.FORM_SHEET` instead.\n */\n FormSheet = 'formSheet',\n /**\n * @deprecated Use `UIImagePickerPresentationStyle.CURRENT_CONTEXT` instead.\n */\n CurrentContext = 'currentContext',\n /**\n * @deprecated Use `UIImagePickerPresentationStyle.OVER_FULL_SCREEN` instead.\n */\n OverFullScreen = 'overFullScreen',\n /**\n * @deprecated Use `UIImagePickerPresentationStyle.OVER_CURRENT_CONTEXT` instead.\n */\n OverCurrentContext = 'overCurrentContext',\n /**\n * @deprecated Use `UIImagePickerPresentationStyle.POPOVER` instead.\n */\n Popover = 'popover',\n /**\n * @deprecated Use `UIImagePickerPresentationStyle.AUTOMATIC` instead.\n */\n Automatic = 'automatic',\n}\n\n// @needsAudit\nexport type ImageInfo = {\n /**\n * URI to the local image or video file (usable as the source of an `Image` element, in the case of\n * an image) and `width` and `height` specify the dimensions of the media.\n */\n uri: string;\n /**\n * Width of the image or video.\n */\n width: number;\n /**\n * Height of the image or video.\n */\n height: number;\n /**\n * The type of the asset.\n */\n type?: 'image' | 'video';\n /**\n * Preferred filename to use when saving this item. This might be `null` when the name is unavailable\n * or user gave limited permission to access the media library.\n *\n * @platform ios\n */\n fileName?: string | null;\n /**\n * File size of the picked image or video, in bytes.\n *\n * @platform ios\n */\n fileSize?: number;\n /**\n * The `exif` field is included if the `exif` option is truthy, and is an object containing the\n * image's EXIF data. The names of this object's properties are EXIF tags and the values are the\n * respective EXIF values for those tags.\n */\n exif?: Record<string, any>;\n /**\n * Included if the `base64` option is truthy, and is a Base64-encoded string of the selected\n * image's JPEG data. If you prepend this with `'data:image/jpeg;base64,'` to create a data URI,\n * you can use it as the source of an `Image` element; for example:\n * ```ts\n * <Image\n * source={{ uri: 'data:image/jpeg;base64,' + launchCameraResult.base64 }}\n * style={{ width: 200, height: 200 }}\n * />\n * ```\n */\n base64?: string;\n /**\n * Length of the video in milliseconds.\n */\n duration?: number;\n /**\n * Boolean flag which shows if request was cancelled. If asset data have been returned this should\n * always be `false`.\n */\n cancelled: boolean;\n};\n\n// @needsAudit\nexport type ImagePickerErrorResult = {\n /**\n * The error code.\n */\n code: string;\n /**\n * The error message.\n */\n message: string;\n /**\n * The exception which caused the error.\n */\n exception?: string;\n};\n\n// @needsAudit\n/**\n * An object returned when the pick action has been cancelled by the user.\n */\nexport type ImagePickerCancelledResult = { cancelled: true };\n\n// @needsAudit\nexport type ImagePickerResult = ImagePickerCancelledResult | ImageInfo;\n\n// @needsAudit @docsMissing\nexport type ImagePickerMultipleResult =\n | ImagePickerCancelledResult\n | { cancelled: false; selected: ImageInfo[] };\n\n// @needsAudit\nexport type ImagePickerOptions = {\n /**\n * Whether to show a UI to edit the image after it is picked. On Android the user can crop and\n * rotate the image and on iOS simply crop it.\n *\n * > Cropping multiple images is not supported - this option is mutually exclusive with `allowsMultipleSelection`.\n * > On iOS, this option is ignored if `allowsMultipleSelection` is enabled.\n *\n * @default false\n * @platform ios\n * @platform android\n */\n allowsEditing?: boolean;\n /**\n * An array with two entries `[x, y]` specifying the aspect ratio to maintain if the user is\n * allowed to edit the image (by passing `allowsEditing: true`). This is only applicable on\n * Android, since on iOS the crop rectangle is always a square.\n */\n aspect?: [number, number];\n /**\n * Specify the quality of compression, from `0` to `1`. `0` means compress for small size,\n * `1` means compress for maximum quality.\n * > Note: If the selected image has been compressed before, the size of the output file may be\n * > bigger than the size of the original image.\n *\n * > Note: On iOS, if a `.bmp` or `.png` image is selected from the library, this option is ignored.\n *\n * @default 0.2\n * @platform ios\n * @platform android\n */\n quality?: number;\n /**\n * Choose what type of media to pick.\n * @default ImagePicker.MediaTypeOptions.Images\n */\n mediaTypes?: MediaTypeOptions;\n /**\n * Whether to also include the EXIF data for the image. On iOS the EXIF data does not include GPS\n * tags in the camera case.\n */\n exif?: boolean;\n /**\n * Whether to also include the image data in Base64 format.\n */\n base64?: boolean;\n /**\n * Specify preset which will be used to compress selected video.\n * @default ImagePicker.VideoExportPreset.Passthrough\n * @platform ios 11+\n * @deprecated See [`videoExportPreset`](https://developer.apple.com/documentation/uikit/uiimagepickercontroller/2890964-videoexportpreset?language=objc)\n * in Apple documentation.\n */\n videoExportPreset?: VideoExportPreset;\n /**\n * Specify the quality of recorded videos. Defaults to the highest quality available for the device.\n * @default ImagePicker.UIImagePickerControllerQualityType.High\n * @platform ios\n */\n videoQuality?: UIImagePickerControllerQualityType;\n /**\n * Whether or not to allow selecting multiple media files at once.\n *\n * > Cropping multiple images is not supported - this option is mutually exclusive with `allowsEditing`.\n * > If this option is enabled, then `allowsEditing` is ignored.\n *\n * @default false\n * @platform ios 14+\n * @platform web\n */\n allowsMultipleSelection?: boolean;\n /**\n * The maximum number of items that user can select. Applicable when `allowsMultipleSelection` is enabled.\n * Setting the value to `0` sets the selection limit to the maximum that the system supports.\n *\n * @platform ios 14+\n * @default 0\n */\n selectionLimit?: number;\n /**\n * Whether to display number badges when assets are selected. The badges are numbered\n * in selection order. Assets are then returned in the exact same order they were selected.\n *\n * > Assets should be returned in the selection order regardless of this option,\n * > but there is no guarantee that it is always true when this option is disabled.\n *\n * @platform ios 15+\n * @default false\n */\n orderedSelection?: boolean;\n /**\n * Maximum duration, in seconds, for video recording. Setting this to `0` disables the limit.\n * Defaults to `0` (no limit).\n * - **On iOS**, when `allowsEditing` is set to `true`, maximum duration is limited to 10 minutes.\n * This limit is applied automatically, if `0` or no value is specified.\n * - **On Android**, effect of this option depends on support of installed camera app.\n * - **On Web** this option has no effect - the limit is browser-dependant.\n */\n videoMaxDuration?: number;\n /**\n * Choose [presentation style](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621355-modalpresentationstyle?language=objc)\n * to customize view during taking photo/video.\n * @default ImagePicker.UIImagePickerPresentationStyle.Automatic\n * @platform ios\n */\n presentationStyle?: UIImagePickerPresentationStyle;\n};\n\n// @needsAudit\nexport type OpenFileBrowserOptions = {\n /**\n * Choose what type of media to pick.\n * @default ImagePicker.MediaTypeOptions.Images\n */\n mediaTypes: MediaTypeOptions;\n // @docsMissing\n capture?: boolean;\n /**\n * Whether or not to allow selecting multiple media files at once.\n * @platform web\n */\n allowsMultipleSelection: boolean;\n /**\n * Whether to also include the image data in Base64 format.\n */\n base64: boolean;\n};\n\n// @needsAudit @docsMissing\nexport type ExpandImagePickerResult<T extends ImagePickerOptions | OpenFileBrowserOptions> =\n T extends {\n allowsMultipleSelection: true;\n }\n ? ImagePickerMultipleResult\n : ImagePickerResult;\n"]}
1
+ {"version":3,"file":"ImagePicker.types.js","sourceRoot":"","sources":["../src/ImagePicker.types.ts"],"names":[],"mappings":"AA0BA,cAAc;AACd,MAAM,CAAN,IAAY,gBAaX;AAbD,WAAY,gBAAgB;IAC1B;;OAEG;IACH,+BAAW,CAAA;IACX;;OAEG;IACH,qCAAiB,CAAA;IACjB;;OAEG;IACH,qCAAiB,CAAA;AACnB,CAAC,EAbW,gBAAgB,KAAhB,gBAAgB,QAa3B;AAED,cAAc;AACd,MAAM,CAAN,IAAY,iBAmEX;AAnED,WAAY,iBAAiB;IAC3B;;;;OAIG;IACH,uEAAe,CAAA;IACf;;;;OAIG;IACH,qEAAc,CAAA;IACd;;;;OAIG;IACH,2EAAiB,CAAA;IACjB;;;;OAIG;IACH,6EAAkB,CAAA;IAClB;;;;OAIG;IACH,yEAAgB,CAAA;IAChB;;;;OAIG;IACH,yEAAgB,CAAA;IAChB;;;;OAIG;IACH,2EAAiB,CAAA;IACjB;;;;OAIG;IACH,6EAAkB,CAAA;IAClB;;;;OAIG;IACH,6EAAkB,CAAA;IAClB;;;;OAIG;IACH,6EAAkB,CAAA;IAClB;;;;OAIG;IACH,8EAAmB,CAAA;AACrB,CAAC,EAnEW,iBAAiB,KAAjB,iBAAiB,QAmE5B;AAED,cAAc;AACd,MAAM,CAAN,IAAY,kCAyBX;AAzBD,WAAY,kCAAkC;IAC5C;;OAEG;IACH,2FAAQ,CAAA;IACR;;OAEG;IACH,+FAAU,CAAA;IACV;;OAEG;IACH,yFAAO,CAAA;IACP;;OAEG;IACH,uGAAc,CAAA;IACd;;OAEG;IACH,+GAAkB,CAAA;IAClB;;OAEG;IACH,6GAAiB,CAAA;AACnB,CAAC,EAzBW,kCAAkC,KAAlC,kCAAkC,QAyB7C;AAED;;;;GAIG;AACH,MAAM,CAAN,IAAY,8BAoEX;AApED,WAAY,8BAA8B;IACxC;;OAEG;IACH,4DAA0B,CAAA;IAC1B;;OAEG;IACH,0DAAwB,CAAA;IACxB;;OAEG;IACH,0DAAwB,CAAA;IACxB;;OAEG;IACH,oEAAkC,CAAA;IAClC;;OAEG;IACH,qEAAmC,CAAA;IACnC;;OAEG;IACH,6EAA2C,CAAA;IAC3C;;OAEG;IACH,qDAAmB,CAAA;IACnB;;;;;OAKG;IACH,yDAAuB,CAAA;IACvB;;OAEG;IACH,2DAAyB,CAAA;IACzB;;OAEG;IACH,yDAAuB,CAAA;IACvB;;OAEG;IACH,yDAAuB,CAAA;IACvB;;OAEG;IACH,mEAAiC,CAAA;IACjC;;OAEG;IACH,mEAAiC,CAAA;IACjC;;OAEG;IACH,2EAAyC,CAAA;IACzC;;OAEG;IACH,qDAAmB,CAAA;IACnB;;OAEG;IACH,yDAAuB,CAAA;AACzB,CAAC,EApEW,8BAA8B,KAA9B,8BAA8B,QAoEzC","sourcesContent":["import { PermissionResponse } from 'expo-modules-core';\n\n// @needsAudit\n/**\n * Alias for `PermissionResponse` type exported by `expo-modules-core`.\n */\nexport type CameraPermissionResponse = PermissionResponse;\n\n// @needsAudit\n/**\n * Extends `PermissionResponse` type exported by `expo-modules-core` and contains additional iOS-specific field.\n */\nexport type MediaLibraryPermissionResponse = PermissionResponse & {\n /**\n * @platform ios\n */\n accessPrivileges?: 'all' | 'limited' | 'none';\n};\n\n// @needsAudit\n/**\n * An alias for the `MediaLibraryPermissionResponse` object.\n * @deprecated Use `ImagePicker.MediaLibraryPermissionResponse` instead.\n */\nexport type CameraRollPermissionResponse = MediaLibraryPermissionResponse;\n\n// @needsAudit\nexport enum MediaTypeOptions {\n /**\n * Images and videos.\n */\n All = 'All',\n /**\n * Only videos.\n */\n Videos = 'Videos',\n /**\n * Only images.\n */\n Images = 'Images',\n}\n\n// @needsAudit\nexport enum VideoExportPreset {\n /**\n * Resolution: __Unchanged__ •\n * Video compression: __None__ •\n * Audio compression: __None__\n */\n Passthrough = 0,\n /**\n * Resolution: __Depends on the device__ •\n * Video compression: __H.264__ •\n * Audio compression: __AAC__\n */\n LowQuality = 1,\n /**\n * Resolution: __Depends on the device__ •\n * Video compression: __H.264__ •\n * Audio compression: __AAC__\n */\n MediumQuality = 2,\n /**\n * Resolution: __Depends on the device__ •\n * Video compression: __H.264__ •\n * Audio compression: __AAC__\n */\n HighestQuality = 3,\n /**\n * Resolution: __640 × 480__ •\n * Video compression: __H.264__ •\n * Audio compression: __AAC__\n */\n H264_640x480 = 4,\n /**\n * Resolution: __960 × 540__ •\n * Video compression: __H.264__ •\n * Audio compression: __AAC__\n */\n H264_960x540 = 5,\n /**\n * Resolution: __1280 × 720__ •\n * Video compression: __H.264__ •\n * Audio compression: __AAC__\n */\n H264_1280x720 = 6,\n /**\n * Resolution: __1920 × 1080__ •\n * Video compression: __H.264__ •\n * Audio compression: __AAC__\n */\n H264_1920x1080 = 7,\n /**\n * Resolution: __3840 × 2160__ •\n * Video compression: __H.264__ •\n * Audio compression: __AAC__\n */\n H264_3840x2160 = 8,\n /**\n * Resolution: __1920 × 1080__ •\n * Video compression: __HEVC__ •\n * Audio compression: __AAC__\n */\n HEVC_1920x1080 = 9,\n /**\n * Resolution: __3840 × 2160__ •\n * Video compression: __HEVC__ •\n * Audio compression: __AAC__\n */\n HEVC_3840x2160 = 10,\n}\n\n// @needsAudit\nexport enum UIImagePickerControllerQualityType {\n /**\n * Highest available resolution.\n */\n High = 0,\n /**\n * Depends on the device.\n */\n Medium = 1,\n /**\n * Depends on the device.\n */\n Low = 2,\n /**\n * 640 × 480\n */\n VGA640x480 = 3,\n /**\n * 1280 × 720\n */\n IFrame1280x720 = 4,\n /**\n * 960 × 540\n */\n IFrame960x540 = 5,\n}\n\n/**\n * Picker presentation style. Its values are directly mapped to the [`UIModalPresentationStyle`](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621355-modalpresentationstyle).\n *\n * @platform ios\n */\nexport enum UIImagePickerPresentationStyle {\n /**\n * A presentation style in which the presented picker covers the screen.\n */\n FULL_SCREEN = 'fullScreen',\n /**\n * A presentation style that partially covers the underlying content.\n */\n PAGE_SHEET = 'pageSheet',\n /**\n * A presentation style that displays the picker centered in the screen.\n */\n FORM_SHEET = 'formSheet',\n /**\n * A presentation style where the picker is displayed over the app's content.\n */\n CURRENT_CONTEXT = 'currentContext',\n /**\n * A presentation style in which the picker view covers the screen.\n */\n OVER_FULL_SCREEN = 'overFullScreen',\n /**\n * A presentation style where the picker is displayed over the app's content.\n */\n OVER_CURRENT_CONTEXT = 'overCurrentContext',\n /**\n * A presentation style where the picker is displayed in a popover view.\n */\n POPOVER = 'popover',\n /**\n * The default presentation style chosen by the system.\n * On older iOS versions, falls back to `WebBrowserPresentationStyle.FullScreen`.\n *\n * @platform ios 13+\n */\n AUTOMATIC = 'automatic',\n /**\n * @deprecated Use `UIImagePickerPresentationStyle.FULL_SCREEN` instead.\n */\n FullScreen = 'fullScreen',\n /**\n * @deprecated Use `UIImagePickerPresentationStyle.PAGE_SHEET` instead.\n */\n PageSheet = 'pageSheet',\n /**\n * @deprecated Use `UIImagePickerPresentationStyle.FORM_SHEET` instead.\n */\n FormSheet = 'formSheet',\n /**\n * @deprecated Use `UIImagePickerPresentationStyle.CURRENT_CONTEXT` instead.\n */\n CurrentContext = 'currentContext',\n /**\n * @deprecated Use `UIImagePickerPresentationStyle.OVER_FULL_SCREEN` instead.\n */\n OverFullScreen = 'overFullScreen',\n /**\n * @deprecated Use `UIImagePickerPresentationStyle.OVER_CURRENT_CONTEXT` instead.\n */\n OverCurrentContext = 'overCurrentContext',\n /**\n * @deprecated Use `UIImagePickerPresentationStyle.POPOVER` instead.\n */\n Popover = 'popover',\n /**\n * @deprecated Use `UIImagePickerPresentationStyle.AUTOMATIC` instead.\n */\n Automatic = 'automatic',\n}\n\n// @needsAudit\nexport type ImageInfo = {\n /**\n * URI to the local image or video file (usable as the source of an `Image` element, in the case of\n * an image) and `width` and `height` specify the dimensions of the media.\n */\n uri: string;\n /**\n * The unique ID that represents the picked image or video, if picked from the library. It can be used\n * by [expo-media-library](./media-library) to manage the picked asset.\n *\n * > This might be `null` when the ID is unavailable or the user gave limited permission to access the media library.\n * > On Android, the ID is unavailable when the user selects a photo by directly browsing file system.\n *\n * @platform ios\n * @platform android\n */\n assetId?: string | null;\n /**\n * Width of the image or video.\n */\n width: number;\n /**\n * Height of the image or video.\n */\n height: number;\n /**\n * The type of the asset.\n */\n type?: 'image' | 'video';\n /**\n * Preferred filename to use when saving this item. This might be `null` when the name is unavailable\n * or user gave limited permission to access the media library.\n *\n * @platform ios\n */\n fileName?: string | null;\n /**\n * File size of the picked image or video, in bytes.\n *\n * @platform ios\n */\n fileSize?: number;\n /**\n * The `exif` field is included if the `exif` option is truthy, and is an object containing the\n * image's EXIF data. The names of this object's properties are EXIF tags and the values are the\n * respective EXIF values for those tags.\n */\n exif?: Record<string, any>;\n /**\n * Included if the `base64` option is truthy, and is a Base64-encoded string of the selected\n * image's JPEG data. If you prepend this with `'data:image/jpeg;base64,'` to create a data URI,\n * you can use it as the source of an `Image` element; for example:\n * ```ts\n * <Image\n * source={{ uri: 'data:image/jpeg;base64,' + launchCameraResult.base64 }}\n * style={{ width: 200, height: 200 }}\n * />\n * ```\n */\n base64?: string;\n /**\n * Length of the video in milliseconds.\n */\n duration?: number;\n /**\n * Boolean flag which shows if request was cancelled. If asset data have been returned this should\n * always be `false`.\n */\n cancelled: boolean;\n};\n\n// @needsAudit\nexport type ImagePickerErrorResult = {\n /**\n * The error code.\n */\n code: string;\n /**\n * The error message.\n */\n message: string;\n /**\n * The exception which caused the error.\n */\n exception?: string;\n};\n\n// @needsAudit\n/**\n * An object returned when the pick action has been cancelled by the user.\n */\nexport type ImagePickerCancelledResult = { cancelled: true };\n\n// @needsAudit\nexport type ImagePickerResult = ImagePickerCancelledResult | ImageInfo;\n\n// @needsAudit @docsMissing\nexport type ImagePickerMultipleResult =\n | ImagePickerCancelledResult\n | { cancelled: false; selected: ImageInfo[] };\n\n// @needsAudit\nexport type ImagePickerOptions = {\n /**\n * Whether to show a UI to edit the image after it is picked. On Android the user can crop and\n * rotate the image and on iOS simply crop it.\n *\n * > Cropping multiple images is not supported - this option is mutually exclusive with `allowsMultipleSelection`.\n * > On iOS, this option is ignored if `allowsMultipleSelection` is enabled.\n *\n * @default false\n * @platform ios\n * @platform android\n */\n allowsEditing?: boolean;\n /**\n * An array with two entries `[x, y]` specifying the aspect ratio to maintain if the user is\n * allowed to edit the image (by passing `allowsEditing: true`). This is only applicable on\n * Android, since on iOS the crop rectangle is always a square.\n */\n aspect?: [number, number];\n /**\n * Specify the quality of compression, from `0` to `1`. `0` means compress for small size,\n * `1` means compress for maximum quality.\n * > Note: If the selected image has been compressed before, the size of the output file may be\n * > bigger than the size of the original image.\n *\n * > Note: On iOS, if a `.bmp` or `.png` image is selected from the library, this option is ignored.\n *\n * @default 0.2\n * @platform ios\n * @platform android\n */\n quality?: number;\n /**\n * Choose what type of media to pick.\n * @default ImagePicker.MediaTypeOptions.Images\n */\n mediaTypes?: MediaTypeOptions;\n /**\n * Whether to also include the EXIF data for the image. On iOS the EXIF data does not include GPS\n * tags in the camera case.\n */\n exif?: boolean;\n /**\n * Whether to also include the image data in Base64 format.\n */\n base64?: boolean;\n /**\n * Specify preset which will be used to compress selected video.\n * @default ImagePicker.VideoExportPreset.Passthrough\n * @platform ios 11+\n * @deprecated See [`videoExportPreset`](https://developer.apple.com/documentation/uikit/uiimagepickercontroller/2890964-videoexportpreset?language=objc)\n * in Apple documentation.\n */\n videoExportPreset?: VideoExportPreset;\n /**\n * Specify the quality of recorded videos. Defaults to the highest quality available for the device.\n * @default ImagePicker.UIImagePickerControllerQualityType.High\n * @platform ios\n */\n videoQuality?: UIImagePickerControllerQualityType;\n /**\n * Whether or not to allow selecting multiple media files at once.\n *\n * > Cropping multiple images is not supported - this option is mutually exclusive with `allowsEditing`.\n * > If this option is enabled, then `allowsEditing` is ignored.\n *\n * @default false\n * @platform ios 14+\n * @platform web\n */\n allowsMultipleSelection?: boolean;\n /**\n * The maximum number of items that user can select. Applicable when `allowsMultipleSelection` is enabled.\n * Setting the value to `0` sets the selection limit to the maximum that the system supports.\n *\n * @platform ios 14+\n * @default 0\n */\n selectionLimit?: number;\n /**\n * Whether to display number badges when assets are selected. The badges are numbered\n * in selection order. Assets are then returned in the exact same order they were selected.\n *\n * > Assets should be returned in the selection order regardless of this option,\n * > but there is no guarantee that it is always true when this option is disabled.\n *\n * @platform ios 15+\n * @default false\n */\n orderedSelection?: boolean;\n /**\n * Maximum duration, in seconds, for video recording. Setting this to `0` disables the limit.\n * Defaults to `0` (no limit).\n * - **On iOS**, when `allowsEditing` is set to `true`, maximum duration is limited to 10 minutes.\n * This limit is applied automatically, if `0` or no value is specified.\n * - **On Android**, effect of this option depends on support of installed camera app.\n * - **On Web** this option has no effect - the limit is browser-dependant.\n */\n videoMaxDuration?: number;\n /**\n * Choose [presentation style](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621355-modalpresentationstyle?language=objc)\n * to customize view during taking photo/video.\n * @default ImagePicker.UIImagePickerPresentationStyle.Automatic\n * @platform ios\n */\n presentationStyle?: UIImagePickerPresentationStyle;\n};\n\n// @needsAudit\nexport type OpenFileBrowserOptions = {\n /**\n * Choose what type of media to pick.\n * @default ImagePicker.MediaTypeOptions.Images\n */\n mediaTypes: MediaTypeOptions;\n // @docsMissing\n capture?: boolean;\n /**\n * Whether or not to allow selecting multiple media files at once.\n * @platform web\n */\n allowsMultipleSelection: boolean;\n /**\n * Whether to also include the image data in Base64 format.\n */\n base64: boolean;\n};\n\n// @needsAudit @docsMissing\nexport type ExpandImagePickerResult<T extends ImagePickerOptions | OpenFileBrowserOptions> =\n T extends {\n allowsMultipleSelection: true;\n }\n ? ImagePickerMultipleResult\n : ImagePickerResult;\n"]}
@@ -26,6 +26,12 @@ internal class MissingCameraPermissionException: Exception {
26
26
  }
27
27
  }
28
28
 
29
+ internal class MissingPhotoLibraryPermissionException: Exception {
30
+ override var reason: String {
31
+ "Missing photo library permission"
32
+ }
33
+ }
34
+
29
35
  internal class CameraUnavailableOnSimulatorException: Exception {
30
36
  override var reason: String {
31
37
  "Camera not available on simulator"
@@ -63,6 +63,7 @@ internal protocol SelectedMediaInfo {
63
63
  Represents a single selected image
64
64
  */
65
65
  internal struct ImageInfo: SelectedMediaInfo {
66
+ let assetId: String?
66
67
  let type: String = "image"
67
68
  let uri: String
68
69
  let width: Double
@@ -76,6 +77,7 @@ internal struct ImageInfo: SelectedMediaInfo {
76
77
  var result: [String: Any] = [
77
78
  "type": type,
78
79
  "uri": uri,
80
+ "assetId": assetId,
79
81
  "width": width,
80
82
  "height": height,
81
83
  "fileName": fileName,
@@ -95,6 +97,7 @@ internal struct ImageInfo: SelectedMediaInfo {
95
97
  Represents a single selected video
96
98
  */
97
99
  internal struct VideoInfo: SelectedMediaInfo {
100
+ let assetId: String?
98
101
  let type: String = "video"
99
102
  let uri: String
100
103
  let width: Double
@@ -107,6 +110,7 @@ internal struct VideoInfo: SelectedMediaInfo {
107
110
  [
108
111
  "type": type,
109
112
  "uri": uri,
113
+ "assetId": assetId,
110
114
  "width": width,
111
115
  "height": height,
112
116
  "fileName": fileName,
@@ -23,41 +23,38 @@ internal struct MediaHandler {
23
23
 
24
24
  @available(iOS 14, *)
25
25
  internal func handleMultipleMedia(_ selection: [PHPickerResult], completion: @escaping (ImagePickerResult) -> Void) {
26
- var results: [String: SelectedMediaInfo] = [:]
26
+ var results = Array<SelectedMediaInfo?>(repeating: nil, count: selection.count)
27
27
 
28
28
  let dispatchGroup = DispatchGroup()
29
29
  let dispatchQueue = DispatchQueue(label: "expo.imagepicker.multipleMediaHandler")
30
30
 
31
- let resultHandler = { (assetId: String?, result: SelectedMediaResult) -> Void in
31
+ let resultHandler = { (index: Int, result: SelectedMediaResult) -> Void in
32
32
  switch result {
33
33
  case .failure(let exception):
34
34
  return completion(.failure(exception))
35
35
  case .success(let mediaInfo):
36
36
  dispatchQueue.async {
37
- results[assetId!] = mediaInfo
37
+ results[index] = mediaInfo
38
38
  dispatchGroup.leave()
39
39
  }
40
40
  }
41
41
  }
42
42
 
43
- for item in selection {
44
- let itemProvider = item.itemProvider
45
- let id = item.assetIdentifier
43
+ for (index, selectedItem) in selection.enumerated() {
44
+ let itemProvider = selectedItem.itemProvider
46
45
 
47
46
  dispatchGroup.enter()
48
47
  if itemProvider.hasItemConformingToTypeIdentifier(UTType.image.identifier) {
49
- handleImage(itemProvider: itemProvider, assetId: id, completion: resultHandler)
48
+ handleImage(from: selectedItem, atIndex: index, completion: resultHandler)
50
49
  } else if itemProvider.hasItemConformingToTypeIdentifier(UTType.movie.identifier) {
51
- handleVideo(itemProvider: itemProvider, assetId: id, completion: resultHandler)
50
+ handleVideo(from: selectedItem, atIndex: index, completion: resultHandler)
52
51
  } else {
53
52
  completion(.failure(InvalidMediaTypeException(itemProvider.registeredTypeIdentifiers.first)))
54
53
  }
55
54
  }
56
55
 
57
56
  dispatchGroup.notify(queue: .main) {
58
- // assign results in the order they were selected
59
- let sortedResults = selection.map { results[$0.assetIdentifier!]! }
60
- completion(.success(ImagePickerMultipleResponse(results: sortedResults)))
57
+ completion(.success(ImagePickerMultipleResponse(results: results.compactMap { $0 } )))
61
58
  }
62
59
  }
63
60
 
@@ -95,7 +92,8 @@ internal struct MediaHandler {
95
92
  shouldReadBase64: self.options.base64)
96
93
 
97
94
  ImageUtils.optionallyReadExifFrom(mediaInfo: mediaInfo, shouldReadExif: self.options.exif) { exif in
98
- let result: ImagePickerSingleResponse = .image(ImageInfo(uri: targetUrl.absoluteString,
95
+ let result: ImagePickerSingleResponse = .image(ImageInfo(assetId: asset?.localIdentifier,
96
+ uri: targetUrl.absoluteString,
99
97
  width: image.size.width,
100
98
  height: image.size.height,
101
99
  fileName: fileName,
@@ -112,15 +110,16 @@ internal struct MediaHandler {
112
110
  }
113
111
 
114
112
  @available(iOS 14, *)
115
- private func handleImage(itemProvider: NSItemProvider,
116
- assetId: String?,
117
- completion: @escaping (String?, SelectedMediaResult) -> Void) {
113
+ private func handleImage(from selectedImage: PHPickerResult,
114
+ atIndex index: Int = -1,
115
+ completion: @escaping (Int, SelectedMediaResult) -> Void) {
116
+ let itemProvider = selectedImage.itemProvider
118
117
  itemProvider.loadDataRepresentation(forTypeIdentifier: UTType.image.identifier) { rawData, error in
119
118
  do {
120
119
  guard error == nil,
121
120
  let rawData = rawData,
122
121
  let image = try? UIImage(data: rawData) else {
123
- return completion(assetId, .failure(FailedToReadImageException().causedBy(error)))
122
+ return completion(index, .failure(FailedToReadImageException().causedBy(error)))
124
123
  }
125
124
 
126
125
  let (imageData, fileExtension) = try ImageUtils.readDataAndFileExtension(image: image,
@@ -140,18 +139,19 @@ internal struct MediaHandler {
140
139
  tryReadingFile: false,
141
140
  shouldReadBase64: self.options.base64)
142
141
 
143
- let result = ImageInfo(uri: targetUrl.absoluteString,
142
+ let result = ImageInfo(assetId: selectedImage.assetIdentifier,
143
+ uri: targetUrl.absoluteString,
144
144
  width: image.size.width,
145
145
  height: image.size.height,
146
146
  fileName: fileName,
147
147
  fileSize: fileSize,
148
148
  base64: base64,
149
149
  exif: exif)
150
- completion(assetId, .success(result))
150
+ completion(index, .success(result))
151
151
  } catch let exception as Exception {
152
- return completion(assetId, .failure(exception))
152
+ return completion(index, .failure(exception))
153
153
  } catch {
154
- return completion(assetId, .failure(UnexpectedException(error)))
154
+ return completion(index, .failure(UnexpectedException(error)))
155
155
  }
156
156
  } // loadObject
157
157
  }
@@ -183,7 +183,8 @@ internal struct MediaHandler {
183
183
  let fileName = asset?.value(forKey: "filename") as? String
184
184
  let fileSize = getFileSize(from: targetUrl)
185
185
 
186
- let result: ImagePickerSingleResponse = .video(VideoInfo(uri: targetUrl.absoluteString,
186
+ let result: ImagePickerSingleResponse = .video(VideoInfo(assetId: asset?.localIdentifier,
187
+ uri: targetUrl.absoluteString,
187
188
  width: dimensions.width,
188
189
  height: dimensions.height,
189
190
  fileName: fileName,
@@ -198,14 +199,15 @@ internal struct MediaHandler {
198
199
  }
199
200
 
200
201
  @available(iOS 14, *)
201
- private func handleVideo(itemProvider: NSItemProvider,
202
- assetId: String?,
203
- completion: @escaping (String?, SelectedMediaResult) -> Void) {
202
+ private func handleVideo(from selectedVideo: PHPickerResult,
203
+ atIndex index: Int = -1,
204
+ completion: @escaping (Int, SelectedMediaResult) -> Void) {
205
+ let itemProvider = selectedVideo.itemProvider
204
206
  itemProvider.loadFileRepresentation(forTypeIdentifier: UTType.movie.identifier) { [self] url, error in
205
207
  do {
206
208
  guard error == nil,
207
209
  let videoUrl = url as? URL else {
208
- return completion(assetId, .failure(FailedToReadVideoException().causedBy(error)))
210
+ return completion(index, .failure(FailedToReadVideoException().causedBy(error)))
209
211
  }
210
212
 
211
213
  // In case of passthrough, we want original file extension, mp4 otherwise
@@ -227,17 +229,17 @@ internal struct MediaHandler {
227
229
  exportPreset: options.videoExportPreset) { result in
228
230
  switch result {
229
231
  case .failure(let exception):
230
- return completion(assetId, .failure(exception))
232
+ return completion(index, .failure(exception))
231
233
  case .success(let targetUrl):
232
234
  let fileName = itemProvider.suggestedName.map { $0 + transcodeFileExtension }
233
- let videoResult = buildVideoResult(for: targetUrl, withName: fileName)
234
- return completion(assetId, videoResult)
235
+ let videoResult = buildVideoResult(for: targetUrl, withName: fileName, assetId: selectedVideo.assetIdentifier)
236
+ return completion(index, videoResult)
235
237
  }
236
238
  }
237
239
  } catch let exception as Exception {
238
- return completion(assetId, .failure(exception))
240
+ return completion(index, .failure(exception))
239
241
  } catch {
240
- return completion(assetId, .failure(UnexpectedException(error)))
242
+ return completion(index, .failure(UnexpectedException(error)))
241
243
  }
242
244
  }
243
245
  }
@@ -255,15 +257,16 @@ internal struct MediaHandler {
255
257
  let url = URL(fileURLWithPath: path)
256
258
  return url
257
259
  }
258
-
259
- private func buildVideoResult(for videoUrl: URL, withName fileName: String? = nil) -> SelectedMediaResult {
260
+
261
+ private func buildVideoResult(for videoUrl: URL, withName fileName: String?, assetId: String?) -> SelectedMediaResult {
260
262
  guard let size = VideoUtils.readSizeFrom(url: videoUrl) else {
261
263
  return .failure(FailedToReadVideoSizeException())
262
264
  }
263
265
  let duration = VideoUtils.readDurationFrom(url: videoUrl)
264
266
  let fileSize = getFileSize(from: videoUrl)
265
267
 
266
- let result = VideoInfo(uri: videoUrl.absoluteString,
268
+ let result = VideoInfo(assetId: assetId,
269
+ uri: videoUrl.absoluteString,
267
270
  width: size.width,
268
271
  height: size.height,
269
272
  fileName: fileName,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-image-picker",
3
- "version": "13.2.1",
3
+ "version": "13.3.0",
4
4
  "description": "Provides access to the system's UI for selecting images and videos from the phone's library or taking a photo with the camera.",
5
5
  "main": "build/ImagePicker.js",
6
6
  "types": "build/ImagePicker.d.ts",
@@ -46,5 +46,5 @@
46
46
  "peerDependencies": {
47
47
  "expo": "*"
48
48
  },
49
- "gitHead": "8151bf48e683a50e942ef213f7978b35c41f5e11"
49
+ "gitHead": "c6678c65b68e45062d49a2deea8e822f69388278"
50
50
  }
@@ -181,7 +181,7 @@ export async function getPendingResultAsync(): Promise<
181
181
  /**
182
182
  * Display the system UI for taking a photo with the camera. Requires `Permissions.CAMERA`.
183
183
  * On Android and iOS 10 `Permissions.CAMERA_ROLL` is also required. On mobile web, this must be
184
- * called immediately in a user interaction like a button press, otherwise the browser will bloc
184
+ * called immediately in a user interaction like a button press, otherwise the browser will block
185
185
  * the request without a warning.
186
186
  * > **Note:** Make sure that you handle `MainActivity` destruction on **Android**. See [ImagePicker.getPendingResultAsync](#imagepickergetpendingresultasync).
187
187
  * > **Notes for Web:** The system UI can only be shown after user activation (e.g. a `Button` press).
@@ -220,6 +220,17 @@ export type ImageInfo = {
220
220
  * an image) and `width` and `height` specify the dimensions of the media.
221
221
  */
222
222
  uri: string;
223
+ /**
224
+ * The unique ID that represents the picked image or video, if picked from the library. It can be used
225
+ * by [expo-media-library](./media-library) to manage the picked asset.
226
+ *
227
+ * > This might be `null` when the ID is unavailable or the user gave limited permission to access the media library.
228
+ * > On Android, the ID is unavailable when the user selects a photo by directly browsing file system.
229
+ *
230
+ * @platform ios
231
+ * @platform android
232
+ */
233
+ assetId?: string | null;
223
234
  /**
224
235
  * Width of the image or video.
225
236
  */