expo-image-picker 14.0.1 → 14.0.2

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,12 @@
10
10
 
11
11
  ### 💡 Others
12
12
 
13
+ ## 14.0.2 — 2022-11-21
14
+
15
+ ### 🐛 Bug fixes
16
+
17
+ - Fix support for animated GIFs on iOS. ([#20034](https://github.com/expo/expo/pull/20034) by [@barthap](https://github.com/barthap))
18
+
13
19
  ## 14.0.1 — 2022-11-08
14
20
 
15
21
  ### 🐛 Bug fixes
@@ -3,7 +3,7 @@ apply plugin: 'kotlin-android'
3
3
  apply plugin: 'maven-publish'
4
4
 
5
5
  group = 'host.exp.exponent'
6
- version = '14.0.1'
6
+ version = '14.0.2'
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 "14.0.1"
77
+ versionName "14.0.2"
78
78
  }
79
79
  lintOptions {
80
80
  abortOnError false
@@ -78,10 +78,12 @@ export declare function launchCameraAsync(options?: ImagePickerOptions): Promise
78
78
  * Requires `Permissions.MEDIA_LIBRARY` on iOS 10 only. On mobile web, this must be called
79
79
  * immediately in a user interaction like a button press, otherwise the browser will block the
80
80
  * request without a warning.
81
- * **Animated GIFs support** If the selected image is an animated GIF, the result image will be an
82
- * animated GIF too if and only if `quality` is set to `undefined` and `allowsEditing` is set to `false`.
81
+ *
82
+ * **Animated GIFs support:** On Android, if the selected image is an animated GIF, the result image will be an
83
+ * animated GIF too if and only if `quality` is explicitly set to `1.0` and `allowsEditing` is set to `false`.
83
84
  * Otherwise compression and/or cropper will pick the first frame of the GIF and return it as the
84
- * result (on Android the result will be a PNG, on iOS GIF).
85
+ * result (on Android the result will be a PNG). On iOS, both quality and cropping are supported.
86
+ *
85
87
  * > **Notes for Web:** The system UI can only be shown after user activation (e.g. a `Button` press).
86
88
  * Therefore, calling `launchImageLibraryAsync` in `componentDidMount`, for example, will **not**
87
89
  * work as intended. The `cancelled` event will not be returned in the browser due to platform
@@ -1 +1 @@
1
- {"version":3,"file":"ImagePicker.d.ts","sourceRoot":"","sources":["../src/ImagePicker.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAChB,oBAAoB,EACpB,qBAAqB,EACrB,kBAAkB,EAInB,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EACL,wBAAwB,EACxB,8BAA8B,EAC9B,iBAAiB,EACjB,gBAAgB,EAChB,sBAAsB,EACtB,gBAAgB,EAChB,kBAAkB,EAClB,iBAAiB,EACjB,uBAAuB,EACvB,SAAS,EACT,yBAAyB,EACzB,0BAA0B,EAC1B,sBAAsB,EACtB,kCAAkC,EAClC,8BAA8B,EAC/B,MAAM,qBAAqB,CAAC;AAsE7B;;;GAGG;AACH,wBAAsB,yBAAyB,IAAI,OAAO,CAAC,wBAAwB,CAAC,CAEnF;AAGD;;;;GAIG;AACH,wBAAsB,+BAA+B,CACnD,SAAS,GAAE,OAAe,GACzB,OAAO,CAAC,8BAA8B,CAAC,CAEzC;AAGD;;;;GAIG;AACH,wBAAsB,6BAA6B,IAAI,OAAO,CAAC,wBAAwB,CAAC,CAEvF;AAGD;;;;GAIG;AACH,wBAAsB,mCAAmC,CACvD,SAAS,GAAE,OAAe,GACzB,OAAO,CAAC,8BAA8B,CAAC,CAGzC;AAGD;;;;;;;;GAQG;AACH,eAAO,MAAM,0BAA0B;;wJAOrC,CAAC;AAGH;;;;;;;;GAQG;AACH,eAAO,MAAM,oBAAoB,4JAG/B,CAAC;AAGH;;;;;;;;;;GAUG;AACH,wBAAsB,qBAAqB,IAAI,OAAO,CACpD,CAAC,iBAAiB,GAAG,sBAAsB,CAAC,EAAE,CAC/C,CAKA;AAGD;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,iBAAiB,CACrC,OAAO,GAAE,kBAAuB,GAC/B,OAAO,CAAC,iBAAiB,CAAC,CAM5B;AAGD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,uBAAuB,CAC3C,OAAO,CAAC,EAAE,kBAAkB,GAC3B,OAAO,CAAC,iBAAiB,CAAC,CAa5B;AAED,OAAO,EACL,gBAAgB,EAChB,kBAAkB,EAClB,iBAAiB,EACjB,sBAAsB,EACtB,gBAAgB,EAChB,iBAAiB,EACjB,wBAAwB,EACxB,8BAA8B,EAC9B,gBAAgB,EAChB,oBAAoB,EACpB,qBAAqB,EACrB,kBAAkB,EAClB,SAAS,EAAE,aAAa;AACxB,yBAAyB,EAAE,aAAa;AACxC,0BAA0B,EAAE,aAAa;AACzC,sBAAsB,EACtB,uBAAuB,EAAE,aAAa;AACtC,kCAAkC,EAClC,8BAA8B,GAC/B,CAAC"}
1
+ {"version":3,"file":"ImagePicker.d.ts","sourceRoot":"","sources":["../src/ImagePicker.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAChB,oBAAoB,EACpB,qBAAqB,EACrB,kBAAkB,EAInB,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EACL,wBAAwB,EACxB,8BAA8B,EAC9B,iBAAiB,EACjB,gBAAgB,EAChB,sBAAsB,EACtB,gBAAgB,EAChB,kBAAkB,EAClB,iBAAiB,EACjB,uBAAuB,EACvB,SAAS,EACT,yBAAyB,EACzB,0BAA0B,EAC1B,sBAAsB,EACtB,kCAAkC,EAClC,8BAA8B,EAC/B,MAAM,qBAAqB,CAAC;AAsE7B;;;GAGG;AACH,wBAAsB,yBAAyB,IAAI,OAAO,CAAC,wBAAwB,CAAC,CAEnF;AAGD;;;;GAIG;AACH,wBAAsB,+BAA+B,CACnD,SAAS,GAAE,OAAe,GACzB,OAAO,CAAC,8BAA8B,CAAC,CAEzC;AAGD;;;;GAIG;AACH,wBAAsB,6BAA6B,IAAI,OAAO,CAAC,wBAAwB,CAAC,CAEvF;AAGD;;;;GAIG;AACH,wBAAsB,mCAAmC,CACvD,SAAS,GAAE,OAAe,GACzB,OAAO,CAAC,8BAA8B,CAAC,CAGzC;AAGD;;;;;;;;GAQG;AACH,eAAO,MAAM,0BAA0B;;wJAOrC,CAAC;AAGH;;;;;;;;GAQG;AACH,eAAO,MAAM,oBAAoB,4JAG/B,CAAC;AAGH;;;;;;;;;;GAUG;AACH,wBAAsB,qBAAqB,IAAI,OAAO,CACpD,CAAC,iBAAiB,GAAG,sBAAsB,CAAC,EAAE,CAC/C,CAKA;AAGD;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,iBAAiB,CACrC,OAAO,GAAE,kBAAuB,GAC/B,OAAO,CAAC,iBAAiB,CAAC,CAM5B;AAGD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,uBAAuB,CAC3C,OAAO,CAAC,EAAE,kBAAkB,GAC3B,OAAO,CAAC,iBAAiB,CAAC,CAa5B;AAED,OAAO,EACL,gBAAgB,EAChB,kBAAkB,EAClB,iBAAiB,EACjB,sBAAsB,EACtB,gBAAgB,EAChB,iBAAiB,EACjB,wBAAwB,EACxB,8BAA8B,EAC9B,gBAAgB,EAChB,oBAAoB,EACpB,qBAAqB,EACrB,kBAAkB,EAClB,SAAS,EAAE,aAAa;AACxB,yBAAyB,EAAE,aAAa;AACxC,0BAA0B,EAAE,aAAa;AACzC,sBAAsB,EACtB,uBAAuB,EAAE,aAAa;AACtC,kCAAkC,EAClC,8BAA8B,GAC/B,CAAC"}
@@ -160,10 +160,12 @@ export async function launchCameraAsync(options = {}) {
160
160
  * Requires `Permissions.MEDIA_LIBRARY` on iOS 10 only. On mobile web, this must be called
161
161
  * immediately in a user interaction like a button press, otherwise the browser will block the
162
162
  * request without a warning.
163
- * **Animated GIFs support** If the selected image is an animated GIF, the result image will be an
164
- * animated GIF too if and only if `quality` is set to `undefined` and `allowsEditing` is set to `false`.
163
+ *
164
+ * **Animated GIFs support:** On Android, if the selected image is an animated GIF, the result image will be an
165
+ * animated GIF too if and only if `quality` is explicitly set to `1.0` and `allowsEditing` is set to `false`.
165
166
  * Otherwise compression and/or cropper will pick the first frame of the GIF and return it as the
166
- * result (on Android the result will be a PNG, on iOS GIF).
167
+ * result (on Android the result will be a PNG). On iOS, both quality and cropping are supported.
168
+ *
167
169
  * > **Notes for Web:** The system UI can only be shown after user activation (e.g. a `Button` press).
168
170
  * Therefore, calling `launchImageLibraryAsync` in `componentDidMount`, for example, will **not**
169
171
  * work as intended. The `cancelled` event will not be returned in the browser due to platform
@@ -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 MediaLibraryPermissionResponse,\n ImagePickerResult,\n ImagePickerAsset,\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\nconst DEPRECATED_RESULT_KEYS = [\n 'uri',\n 'assetId',\n 'width',\n 'height',\n 'type',\n 'exif',\n 'base64',\n 'duration',\n 'fileName',\n 'fileSize',\n];\nfunction mergeDeprecatedResult(result: ImagePickerResult): ImagePickerResult {\n const firstAsset = result.assets?.[0];\n const deprecatedResult = {\n ...result,\n get cancelled() {\n console.warn(\n 'Key \"cancelled\" in the image picker result is deprecated and will be removed in SDK 48, use \"canceled\" instead'\n );\n return this.canceled;\n },\n };\n for (const key of DEPRECATED_RESULT_KEYS) {\n Object.defineProperty(deprecatedResult, key, {\n get() {\n console.warn(\n `Key \"${key}\" in the image picker result is deprecated and will be removed in SDK 48, you can access selected assets through the \"assets\" array instead`\n );\n return firstAsset?.[key];\n },\n });\n }\n return deprecatedResult;\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](#camerapermissionresponse).\n */\nexport async function getCameraPermissionsAsync(): Promise<CameraPermissionResponse> {\n return ExponentImagePicker.getCameraPermissionsAsync();\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// @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 A promise that resolves to an object with `canceled` and `assets` fields.\n * When the user canceled the action the `assets` is always `null`, otherwise it's an array of\n * the selected media assets which have a form of [`ImagePickerAsset`](#imagepickerasset).\n */\nexport async function launchCameraAsync(\n options: ImagePickerOptions = {}\n): Promise<ImagePickerResult> {\n if (!ExponentImagePicker.launchCameraAsync) {\n throw new UnavailabilityError('ImagePicker', 'launchCameraAsync');\n }\n const result = await ExponentImagePicker.launchCameraAsync(validateOptions(options));\n return mergeDeprecatedResult(result);\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 A promise that resolves to an object with `canceled` and `assets` fields.\n * When the user canceled the action the `assets` is always `null`, otherwise it's an array of\n * the selected media assets which have a form of [`ImagePickerAsset`](#imagepickerasset).\n */\nexport async function launchImageLibraryAsync(\n options?: ImagePickerOptions\n): Promise<ImagePickerResult> {\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 const result = await ExponentImagePicker.launchImageLibraryAsync(options ?? {});\n return mergeDeprecatedResult(result);\n}\n\nexport {\n MediaTypeOptions,\n ImagePickerOptions,\n ImagePickerResult,\n ImagePickerErrorResult,\n ImagePickerAsset,\n VideoExportPreset,\n CameraPermissionResponse,\n MediaLibraryPermissionResponse,\n PermissionStatus,\n PermissionExpiration,\n PermissionHookOptions,\n PermissionResponse,\n ImageInfo, // deprecated\n ImagePickerMultipleResult, // deprecated\n ImagePickerCancelledResult, // deprecated\n OpenFileBrowserOptions,\n ExpandImagePickerResult, // deprecated\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 MediaLibraryPermissionResponse,\n ImagePickerResult,\n ImagePickerAsset,\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\nconst DEPRECATED_RESULT_KEYS = [\n 'uri',\n 'assetId',\n 'width',\n 'height',\n 'type',\n 'exif',\n 'base64',\n 'duration',\n 'fileName',\n 'fileSize',\n];\nfunction mergeDeprecatedResult(result: ImagePickerResult): ImagePickerResult {\n const firstAsset = result.assets?.[0];\n const deprecatedResult = {\n ...result,\n get cancelled() {\n console.warn(\n 'Key \"cancelled\" in the image picker result is deprecated and will be removed in SDK 48, use \"canceled\" instead'\n );\n return this.canceled;\n },\n };\n for (const key of DEPRECATED_RESULT_KEYS) {\n Object.defineProperty(deprecatedResult, key, {\n get() {\n console.warn(\n `Key \"${key}\" in the image picker result is deprecated and will be removed in SDK 48, you can access selected assets through the \"assets\" array instead`\n );\n return firstAsset?.[key];\n },\n });\n }\n return deprecatedResult;\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](#camerapermissionresponse).\n */\nexport async function getCameraPermissionsAsync(): Promise<CameraPermissionResponse> {\n return ExponentImagePicker.getCameraPermissionsAsync();\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// @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 A promise that resolves to an object with `canceled` and `assets` fields.\n * When the user canceled the action the `assets` is always `null`, otherwise it's an array of\n * the selected media assets which have a form of [`ImagePickerAsset`](#imagepickerasset).\n */\nexport async function launchCameraAsync(\n options: ImagePickerOptions = {}\n): Promise<ImagePickerResult> {\n if (!ExponentImagePicker.launchCameraAsync) {\n throw new UnavailabilityError('ImagePicker', 'launchCameraAsync');\n }\n const result = await ExponentImagePicker.launchCameraAsync(validateOptions(options));\n return mergeDeprecatedResult(result);\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 *\n * **Animated GIFs support:** On Android, if the selected image is an animated GIF, the result image will be an\n * animated GIF too if and only if `quality` is explicitly set to `1.0` 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, both quality and cropping are supported.\n *\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 A promise that resolves to an object with `canceled` and `assets` fields.\n * When the user canceled the action the `assets` is always `null`, otherwise it's an array of\n * the selected media assets which have a form of [`ImagePickerAsset`](#imagepickerasset).\n */\nexport async function launchImageLibraryAsync(\n options?: ImagePickerOptions\n): Promise<ImagePickerResult> {\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 const result = await ExponentImagePicker.launchImageLibraryAsync(options ?? {});\n return mergeDeprecatedResult(result);\n}\n\nexport {\n MediaTypeOptions,\n ImagePickerOptions,\n ImagePickerResult,\n ImagePickerErrorResult,\n ImagePickerAsset,\n VideoExportPreset,\n CameraPermissionResponse,\n MediaLibraryPermissionResponse,\n PermissionStatus,\n PermissionExpiration,\n PermissionHookOptions,\n PermissionResponse,\n ImageInfo, // deprecated\n ImagePickerMultipleResult, // deprecated\n ImagePickerCancelledResult, // deprecated\n OpenFileBrowserOptions,\n ExpandImagePickerResult, // deprecated\n UIImagePickerControllerQualityType,\n UIImagePickerPresentationStyle,\n};\n"]}
@@ -5,6 +5,8 @@ import MobileCoreServices
5
5
  import PhotosUI
6
6
 
7
7
  internal let DEFAULT_QUALITY = 0.2
8
+ internal let MAXIMUM_QUALITY = 1.0
9
+
8
10
  internal let UNLIMITED_SELECTION = 0
9
11
  internal let SINGLE_SELECTION = 1
10
12
 
@@ -126,6 +126,7 @@ internal struct MediaHandler {
126
126
  }
127
127
 
128
128
  let (imageData, fileExtension) = try ImageUtils.readDataAndFileExtension(image: image,
129
+ rawData: rawData,
129
130
  itemProvider: itemProvider,
130
131
  options: self.options)
131
132
 
@@ -345,11 +346,17 @@ private struct ImageUtils {
345
346
  return (nil, ".bmp")
346
347
 
347
348
  case .some(let s) where s.contains("ext=GIF"):
349
+ var rawData: Data?
350
+ if let imgUrl = mediaInfo[.imageURL] as? URL {
351
+ rawData = try? Data(contentsOf: imgUrl)
352
+ }
353
+ let inputData = rawData ?? image.jpegData(compressionQuality: compressionQuality)
348
354
  let metadata = mediaInfo[.mediaMetadata] as? [String: Any]
349
- let gifData = try getGifDataFrom(image: image,
355
+ let cropRect = options.allowsEditing ? mediaInfo[.cropRect] as? CGRect : nil
356
+ let gifData = try processGifData(inputData: inputData,
350
357
  compressionQuality: options.quality,
351
- initialMetadata: metadata)
352
-
358
+ initialMetadata: metadata,
359
+ cropRect: cropRect)
353
360
  return (gifData, ".gif")
354
361
  default:
355
362
  let data = image.jpegData(compressionQuality: compressionQuality)
@@ -360,6 +367,7 @@ private struct ImageUtils {
360
367
  @available(iOS 14, *)
361
368
  static func readDataAndFileExtension(
362
369
  image: UIImage,
370
+ rawData: Data,
363
371
  itemProvider: NSItemProvider,
364
372
  options: ImagePickerOptions
365
373
  ) throws -> (imageData: Data?, fileExtension: String) {
@@ -371,7 +379,7 @@ private struct ImageUtils {
371
379
  let data = image.pngData()
372
380
  return (data, ".png")
373
381
  case UTType.gif.identifier:
374
- let gifData = try getGifDataFrom(image: image,
382
+ let gifData = try processGifData(inputData: rawData,
375
383
  compressionQuality: options.quality,
376
384
  initialMetadata: nil)
377
385
  return (gifData, ".gif")
@@ -508,37 +516,54 @@ private struct ImageUtils {
508
516
  return exif
509
517
  }
510
518
 
511
- static func getGifDataFrom(image: UIImage,
512
- compressionQuality quality: Double?,
513
- initialMetadata: [String: Any]?) throws -> Data? {
514
- guard let data = image.jpegData(compressionQuality: quality ?? DEFAULT_QUALITY) else {
515
- throw FailedToReadImageDataException()
519
+ static func processGifData(
520
+ inputData: Data?,
521
+ compressionQuality quality: Double?,
522
+ initialMetadata: [String: Any]?,
523
+ cropRect: CGRect? = nil
524
+ ) throws -> Data? {
525
+ // for uncropped, maximum quality image we can just pass through the raw data
526
+ if cropRect == nil,
527
+ quality == nil || (quality ?? -1) >= MAXIMUM_QUALITY {
528
+ return inputData
516
529
  }
517
530
 
531
+ guard let sourceData = inputData,
532
+ let imageSource = CGImageSourceCreateWithData(sourceData as CFData, nil)
533
+ else {
534
+ throw FailedToReadImageException()
535
+ }
536
+
537
+ let gifProperties = CGImageSourceCopyProperties(imageSource, nil) as? [String: Any]
538
+ let frameCount = CGImageSourceGetCount(imageSource)
539
+
518
540
  let destinationData = NSMutableData()
519
- guard let imageDestination = CGImageDestinationCreateWithData(destinationData, kUTTypeGIF, 1, nil),
520
- let cgImage = image.cgImage
541
+ guard let imageDestination = CGImageDestinationCreateWithData(destinationData, kUTTypeGIF, frameCount, nil)
521
542
  else {
522
543
  throw FailedToCreateGifException()
523
544
  }
524
545
 
525
- var metadata: [String: Any] = initialMetadata ?? [:]
526
- if initialMetadata == nil,
527
- let cgImageSource = CGImageSourceCreateWithData(data as CFData, nil),
528
- let properties = CGImageSourceCopyPropertiesAtIndex(cgImageSource, 0, nil) as? [String: Any] {
529
- metadata = properties
530
- }
546
+ let gifMetadata = initialMetadata ?? gifProperties
547
+ CGImageDestinationSetProperties(imageDestination, gifMetadata as CFDictionary?)
531
548
 
532
- if quality != nil {
533
- metadata[kCGImageDestinationLossyCompressionQuality as String] = quality
549
+ for frameIndex in 0 ..< frameCount {
550
+ guard var cgImage = CGImageSourceCreateImageAtIndex(imageSource, frameIndex, nil),
551
+ var frameProperties = CGImageSourceCopyPropertiesAtIndex(imageSource, frameIndex, nil) as? [String: Any]
552
+ else {
553
+ throw FailedToCreateGifException()
554
+ }
555
+ if cropRect != nil {
556
+ cgImage = cgImage.cropping(to: cropRect!)!
557
+ }
558
+ if quality != nil {
559
+ frameProperties[kCGImageDestinationLossyCompressionQuality as String] = quality
560
+ }
561
+ CGImageDestinationAddImage(imageDestination, cgImage, frameProperties as CFDictionary)
534
562
  }
535
563
 
536
- CGImageDestinationAddImage(imageDestination, cgImage, metadata as CFDictionary)
537
-
538
564
  if !CGImageDestinationFinalize(imageDestination) {
539
565
  throw FailedToExportGifException()
540
566
  }
541
-
542
567
  return destinationData as Data
543
568
  }
544
569
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-image-picker",
3
- "version": "14.0.1",
3
+ "version": "14.0.2",
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",
@@ -45,5 +45,5 @@
45
45
  "peerDependencies": {
46
46
  "expo": "*"
47
47
  },
48
- "gitHead": "4cfefb886fd394c376290f24dabf987cdd6e6ca0"
48
+ "gitHead": "7cc0ce2b21febcaf96e875d8a8aff10b320e1b5f"
49
49
  }
@@ -225,10 +225,12 @@ export async function launchCameraAsync(
225
225
  * Requires `Permissions.MEDIA_LIBRARY` on iOS 10 only. On mobile web, this must be called
226
226
  * immediately in a user interaction like a button press, otherwise the browser will block the
227
227
  * request without a warning.
228
- * **Animated GIFs support** If the selected image is an animated GIF, the result image will be an
229
- * animated GIF too if and only if `quality` is set to `undefined` and `allowsEditing` is set to `false`.
228
+ *
229
+ * **Animated GIFs support:** On Android, if the selected image is an animated GIF, the result image will be an
230
+ * animated GIF too if and only if `quality` is explicitly set to `1.0` and `allowsEditing` is set to `false`.
230
231
  * Otherwise compression and/or cropper will pick the first frame of the GIF and return it as the
231
- * result (on Android the result will be a PNG, on iOS GIF).
232
+ * result (on Android the result will be a PNG). On iOS, both quality and cropping are supported.
233
+ *
232
234
  * > **Notes for Web:** The system UI can only be shown after user activation (e.g. a `Button` press).
233
235
  * Therefore, calling `launchImageLibraryAsync` in `componentDidMount`, for example, will **not**
234
236
  * work as intended. The `cancelled` event will not be returned in the browser due to platform