expo-image-picker 13.0.0 → 13.1.1

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,20 @@
10
10
 
11
11
  ### 💡 Others
12
12
 
13
+ ## 13.1.1 — 2022-04-27
14
+
15
+ _This version does not introduce any user-facing changes._
16
+
17
+ ## 13.1.0 — 2022-04-25
18
+
19
+ ### 🎉 New features
20
+
21
+ - [plugin] Added ability to disable permissions. ([#17168](https://github.com/expo/expo/pull/17168) by [@EvanBacon](https://github.com/EvanBacon))
22
+
23
+ ## 13.0.1 — 2022-04-20
24
+
25
+ _This version does not introduce any user-facing changes._
26
+
13
27
  ## 13.0.0 — 2022-04-18
14
28
 
15
29
  ### 🛠 Breaking changes
package/README.md CHANGED
@@ -23,6 +23,8 @@ expo install expo-image-picker
23
23
 
24
24
  ### Configure for iOS
25
25
 
26
+ > This is only required for usage in bare React Native apps.
27
+
26
28
  Add `NSPhotoLibraryUsageDescription`, `NSCameraUsageDescription`, and `NSMicrophoneUsageDescription` keys to your `Info.plist`:
27
29
 
28
30
  ```xml
@@ -38,6 +40,8 @@ Run `npx pod-install` after installing the npm package.
38
40
 
39
41
  ### Configure for Android
40
42
 
43
+ > This is only required for usage in bare React Native apps.
44
+
41
45
  This package automatically adds the `CAMERA`, `READ_EXTERNAL_STORAGE`, and `WRITE_EXTERNAL_STORAGE` permissions. They are used when picking images from the camera directly, or from the camera roll.
42
46
 
43
47
  ```xml
@@ -47,13 +51,49 @@ This package automatically adds the `CAMERA`, `READ_EXTERNAL_STORAGE`, and `WRIT
47
51
  <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
48
52
  ```
49
53
 
50
- In `AndroidManifest.xml` add the following `activity` within `application`:
54
+ ## Config Plugin
51
55
 
52
- ```xml
53
- <activity
54
- android:name="com.canhub.cropper.CropImageActivity"
55
- android:theme="@style/Base.Theme.AppCompat">
56
- </activity>
56
+ > This plugin is applied automatically in EAS Build, only add the config plugin if you want to pass in extra properties.
57
+
58
+ After installing this npm package, add the [config plugin](https://docs.expo.io/guides/config-plugins/) to the [`plugins`](https://docs.expo.io/versions/latest/config/app/#plugins) array of your `app.json` or `app.config.js`:
59
+
60
+ ```json
61
+ {
62
+ "expo": {
63
+ "plugins": ["expo-image-picker"]
64
+ }
65
+ }
66
+ ```
67
+
68
+ Next, rebuild your app as described in the ["Adding custom native code"](https://docs.expo.io/workflow/customizing/) guide.
69
+
70
+ ### API
71
+
72
+ The plugin provides props for extra customization. Every time you change the props or plugins, you'll need to rebuild (and `prebuild`) the native app. If no extra properties are added, defaults will be used.
73
+
74
+ - `photosPermission` (_string | false_): Sets the iOS `NSPhotoLibraryUsageDescription` permission message to the `Info.plist`. Setting `false` will skip adding the permission on iOS and **does not** skip the permission on Android. Defaults to `Allow $(PRODUCT_NAME) to access your photos`.
75
+ - `cameraPermission` (_string | false_): Sets the iOS `NSCameraUsageDescription` permission message to the `Info.plist`. Setting `false` will skip adding the permission on iOS and **does not** skip the permission on Android. Defaults to `Allow $(PRODUCT_NAME) to access your camera`.
76
+ - `microphonePermission` (_string | false_): Sets the iOS `NSCameraUsageDescription` permission message to the `Info.plist`. Setting `false` will skip adding the permission on iOS and skips adding the `android.permission.RECORD_AUDIO` Android permission. Defaults to `Allow $(PRODUCT_NAME) to access your photos`.
77
+
78
+ ### Example
79
+
80
+ ```json
81
+ {
82
+ "expo": {
83
+ "plugins": [
84
+ [
85
+ "expo-image-picker",
86
+ {
87
+ "photosPermission": "custom photos permission",
88
+ "cameraPermission": "Allow $(PRODUCT_NAME) to open the camera",
89
+
90
+ "//": "Disables the microphone permission",
91
+ "microphonePermission": false
92
+ }
93
+ ]
94
+ ]
95
+ }
96
+ }
57
97
  ```
58
98
 
59
99
  # Contributing
@@ -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.0.0'
6
+ version = '13.1.1'
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.0.0"
77
+ versionName "13.1.1"
78
78
  }
79
79
  lintOptions {
80
80
  abortOnError false
@@ -140,21 +140,17 @@ public class ImagePickerModule: Module, OnMediaPickingResultHandler {
140
140
  func didPickMedia(mediaInfo: MediaInfo) {
141
141
  guard let options = self.currentPickingContext?.options,
142
142
  let promise = self.currentPickingContext?.promise else {
143
- self.appContext?.logger?.warn("Picking operation context has been lost.")
143
+ NSLog("Picking operation context has been lost.")
144
144
  return
145
145
  }
146
146
  guard let fileSystem = self.appContext?.fileSystem else {
147
147
  return promise.reject(FileSystemModuleNotFoundException())
148
148
  }
149
- guard let logger = self.appContext?.logger else {
150
- return promise.reject(LoggerModuleNotFoundException())
151
- }
152
149
 
153
150
  // Cleanup the currently stored picking context
154
151
  self.currentPickingContext = nil
155
152
 
156
153
  let mediaHandler = MediaHandler(fileSystem: fileSystem,
157
- logger: logger,
158
154
  options: options)
159
155
  mediaHandler.handleMedia(mediaInfo) { result -> Void in
160
156
  switch result {
@@ -6,7 +6,6 @@ import Photos
6
6
 
7
7
  internal struct MediaHandler {
8
8
  internal weak var fileSystem: EXFileSystemInterface?
9
- internal weak var logger: EXLogManager?
10
9
  internal let options: ImagePickerOptions
11
10
 
12
11
  internal func handleMedia(_ mediaInfo: MediaInfo, completion: @escaping (AsyncResult) -> Void) {
@@ -48,7 +47,7 @@ internal struct MediaHandler {
48
47
  tryReadingFile: fileWasCopied,
49
48
  shouldReadBase64: self.options.base64)
50
49
 
51
- ImageUtils.optionallyReadExifFrom(mediaInfo: mediaInfo, logger: self.logger, shouldReadExif: self.options.exif) { exif in
50
+ ImageUtils.optionallyReadExifFrom(mediaInfo: mediaInfo, shouldReadExif: self.options.exif) { exif in
52
51
  let result: ImagePickerResponse = .image(ImageInfo(uri: targetUrl.absoluteString,
53
52
  width: image.size.width,
54
53
  height: image.size.height,
@@ -254,7 +253,6 @@ private struct ImageUtils {
254
253
 
255
254
  static func optionallyReadExifFrom(
256
255
  mediaInfo: MediaInfo,
257
- logger: EXLogManager?,
258
256
  shouldReadExif: Bool,
259
257
  completion: @escaping (_ result: [String: Any]?) -> Void
260
258
  ) {
@@ -270,14 +268,14 @@ private struct ImageUtils {
270
268
  }
271
269
 
272
270
  guard let imageUrl = mediaInfo[.referenceURL] as? URL else {
273
- logger?.info("Could not fetch metadata for image")
271
+ NSLog("Could not fetch metadata for image")
274
272
  return completion(nil)
275
273
  }
276
274
 
277
275
  let assets = PHAsset.fetchAssets(withALAssetURLs: [imageUrl], options: nil)
278
276
 
279
277
  guard let asset = assets.firstObject else {
280
- logger?.info("Could not fetch metadata for image '\(imageUrl.absoluteString)'.")
278
+ NSLog("Could not fetch metadata for image '\(imageUrl.absoluteString)'.")
281
279
  return completion(nil)
282
280
  }
283
281
 
@@ -287,7 +285,7 @@ private struct ImageUtils {
287
285
  guard let imageUrl = input?.fullSizeImageURL,
288
286
  let properties = CIImage(contentsOf: imageUrl)?.properties
289
287
  else {
290
- logger?.info("Could not fetch metadata for '\(imageUrl.absoluteString)'.")
288
+ NSLog("Could not fetch metadata for '\(imageUrl.absoluteString)'.")
291
289
  return completion(nil)
292
290
  }
293
291
  let exif = ImageUtils.readExifFrom(imageMetadata: properties)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-image-picker",
3
- "version": "13.0.0",
3
+ "version": "13.1.1",
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": "22dce752354bb429c84851bc4389abe47a766b1f"
49
+ "gitHead": "be6e0e7ea3f7fd1ab8368137260a7ad1b1bf12a8"
50
50
  }
@@ -1,8 +1,10 @@
1
- import { ConfigPlugin, AndroidConfig } from '@expo/config-plugins';
2
- export declare function setImagePickerManifestActivity(androidManifest: AndroidConfig.Manifest.AndroidManifest): AndroidConfig.Manifest.AndroidManifest;
3
- declare const _default: ConfigPlugin<void | {
4
- photosPermission?: string | undefined;
5
- cameraPermission?: string | undefined;
6
- microphonePermission?: string | undefined;
7
- }>;
1
+ import { ConfigPlugin, InfoPlist } from '@expo/config-plugins';
2
+ declare type Props = {
3
+ photosPermission?: string | false;
4
+ cameraPermission?: string | false;
5
+ microphonePermission?: string | false;
6
+ };
7
+ export declare function setImagePickerInfoPlist(infoPlist: InfoPlist, { cameraPermission, microphonePermission, photosPermission }: Props): InfoPlist;
8
+ export declare const withAndroidImagePickerPermissions: ConfigPlugin<Props | void>;
9
+ declare const _default: ConfigPlugin<void | Props>;
8
10
  export default _default;
@@ -1,56 +1,71 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.setImagePickerManifestActivity = void 0;
3
+ exports.withAndroidImagePickerPermissions = exports.setImagePickerInfoPlist = void 0;
4
4
  const config_plugins_1 = require("@expo/config-plugins");
5
5
  const pkg = require('expo-image-picker/package.json');
6
6
  const CAMERA_USAGE = 'Allow $(PRODUCT_NAME) to access your camera';
7
7
  const MICROPHONE_USAGE = 'Allow $(PRODUCT_NAME) to access your microphone';
8
8
  const READ_PHOTOS_USAGE = 'Allow $(PRODUCT_NAME) to access your photos';
9
- function setImagePickerManifestActivity(androidManifest) {
10
- const app = config_plugins_1.AndroidConfig.Manifest.getMainApplicationOrThrow(androidManifest);
11
- if (!app.activity) {
12
- app.activity = [];
13
- }
14
- if (!app.activity.find(({ $ }) => $['android:name'] === 'com.canhub.cropper.CropImageActivity')) {
15
- app.activity.push({
16
- $: {
17
- 'android:name': 'com.canhub.cropper.CropImageActivity',
18
- 'android:theme': '@style/Base.Theme.AppCompat',
19
- },
20
- });
9
+ function setImagePickerInfoPlist(infoPlist, { cameraPermission, microphonePermission, photosPermission }) {
10
+ if (photosPermission === false) {
11
+ delete infoPlist.NSPhotoLibraryUsageDescription;
12
+ }
13
+ else {
14
+ infoPlist.NSPhotoLibraryUsageDescription =
15
+ photosPermission || infoPlist.NSPhotoLibraryUsageDescription || READ_PHOTOS_USAGE;
16
+ }
17
+ if (cameraPermission === false) {
18
+ delete infoPlist.NSCameraUsageDescription;
19
+ }
20
+ else {
21
+ infoPlist.NSCameraUsageDescription =
22
+ cameraPermission || infoPlist.NSCameraUsageDescription || CAMERA_USAGE;
21
23
  }
22
- return androidManifest;
24
+ if (microphonePermission === false) {
25
+ delete infoPlist.NSMicrophoneUsageDescription;
26
+ }
27
+ else {
28
+ infoPlist.NSMicrophoneUsageDescription =
29
+ microphonePermission || infoPlist.NSMicrophoneUsageDescription || MICROPHONE_USAGE;
30
+ }
31
+ return infoPlist;
23
32
  }
24
- exports.setImagePickerManifestActivity = setImagePickerManifestActivity;
25
- const withImagePickerManifestActivity = (config) => {
26
- // This plugin has no ability to remove the activity that it adds.
27
- return (0, config_plugins_1.withAndroidManifest)(config, async (config) => {
28
- config.modResults = setImagePickerManifestActivity(config.modResults);
29
- return config;
30
- });
33
+ exports.setImagePickerInfoPlist = setImagePickerInfoPlist;
34
+ const withAndroidImagePickerPermissions = (config, { cameraPermission, microphonePermission } = {}) => {
35
+ if (microphonePermission !== false) {
36
+ config = config_plugins_1.AndroidConfig.Permissions.withPermissions(config, ['android.permission.RECORD_AUDIO']);
37
+ }
38
+ // If the user manually sets any of the permissions to `false`, then we should block the permissions to ensure no
39
+ // package can add them.
40
+ config = config_plugins_1.AndroidConfig.Permissions.withBlockedPermissions(config, [
41
+ microphonePermission === false && 'android.permission.RECORD_AUDIO',
42
+ cameraPermission === false && 'android.permission.CAMERA',
43
+ ].filter(Boolean));
44
+ // NOTE(EvanBacon): It's unclear if we should block the WRITE_EXTERNAL_STORAGE/READ_EXTERNAL_STORAGE permissions since
45
+ // they're used for many other things besides image picker.
46
+ return config;
31
47
  };
48
+ exports.withAndroidImagePickerPermissions = withAndroidImagePickerPermissions;
32
49
  const withImagePicker = (config, { photosPermission, cameraPermission, microphonePermission } = {}) => {
33
- if (!config.ios)
34
- config.ios = {};
35
- if (!config.ios.infoPlist)
36
- config.ios.infoPlist = {};
37
- config.ios.infoPlist.NSPhotoLibraryUsageDescription =
38
- photosPermission || config.ios.infoPlist.NSPhotoLibraryUsageDescription || READ_PHOTOS_USAGE;
39
- config.ios.infoPlist.NSCameraUsageDescription =
40
- cameraPermission || config.ios.infoPlist.NSCameraUsageDescription || CAMERA_USAGE;
41
- config.ios.infoPlist.NSMicrophoneUsageDescription =
42
- microphonePermission || config.ios.infoPlist.NSMicrophoneUsageDescription || MICROPHONE_USAGE;
43
- return (0, config_plugins_1.withPlugins)(config, [
44
- [
45
- config_plugins_1.AndroidConfig.Permissions.withPermissions,
46
- [
47
- 'android.permission.CAMERA',
48
- 'android.permission.READ_EXTERNAL_STORAGE',
49
- 'android.permission.WRITE_EXTERNAL_STORAGE',
50
- 'android.permission.RECORD_AUDIO',
51
- ],
52
- ],
53
- withImagePickerManifestActivity,
54
- ]);
50
+ config = (0, config_plugins_1.withInfoPlist)(config, (config) => {
51
+ config.modResults = setImagePickerInfoPlist(config.modResults, {
52
+ photosPermission,
53
+ cameraPermission,
54
+ microphonePermission,
55
+ });
56
+ return config;
57
+ });
58
+ if (microphonePermission !== false) {
59
+ config = config_plugins_1.AndroidConfig.Permissions.withPermissions(config, ['android.permission.RECORD_AUDIO']);
60
+ }
61
+ // If the user manually sets any of the permissions to `false`, then we should block the permissions to ensure no
62
+ // package can add them.
63
+ config = config_plugins_1.AndroidConfig.Permissions.withBlockedPermissions(config, [
64
+ microphonePermission === false && 'android.permission.RECORD_AUDIO',
65
+ cameraPermission === false && 'android.permission.CAMERA',
66
+ ].filter(Boolean));
67
+ // NOTE(EvanBacon): It's unclear if we should block the WRITE_EXTERNAL_STORAGE/READ_EXTERNAL_STORAGE permissions since
68
+ // they're used for many other things besides image picker.
69
+ return config;
55
70
  };
56
71
  exports.default = (0, config_plugins_1.createRunOncePlugin)(withImagePicker, pkg.name, pkg.version);
@@ -1,9 +1,9 @@
1
1
  import {
2
+ AndroidConfig,
2
3
  ConfigPlugin,
3
4
  createRunOncePlugin,
4
- withAndroidManifest,
5
- withPlugins,
6
- AndroidConfig,
5
+ InfoPlist,
6
+ withInfoPlist,
7
7
  } from '@expo/config-plugins';
8
8
 
9
9
  const pkg = require('expo-image-picker/package.json');
@@ -12,60 +12,92 @@ const CAMERA_USAGE = 'Allow $(PRODUCT_NAME) to access your camera';
12
12
  const MICROPHONE_USAGE = 'Allow $(PRODUCT_NAME) to access your microphone';
13
13
  const READ_PHOTOS_USAGE = 'Allow $(PRODUCT_NAME) to access your photos';
14
14
 
15
- export function setImagePickerManifestActivity(
16
- androidManifest: AndroidConfig.Manifest.AndroidManifest
17
- ): AndroidConfig.Manifest.AndroidManifest {
18
- const app = AndroidConfig.Manifest.getMainApplicationOrThrow(androidManifest);
19
- if (!app.activity) {
20
- app.activity = [];
15
+ type Props = {
16
+ photosPermission?: string | false;
17
+ cameraPermission?: string | false;
18
+ microphonePermission?: string | false;
19
+ };
20
+
21
+ export function setImagePickerInfoPlist(
22
+ infoPlist: InfoPlist,
23
+ { cameraPermission, microphonePermission, photosPermission }: Props
24
+ ): InfoPlist {
25
+ if (photosPermission === false) {
26
+ delete infoPlist.NSPhotoLibraryUsageDescription;
27
+ } else {
28
+ infoPlist.NSPhotoLibraryUsageDescription =
29
+ photosPermission || infoPlist.NSPhotoLibraryUsageDescription || READ_PHOTOS_USAGE;
21
30
  }
22
- if (!app.activity.find(({ $ }) => $['android:name'] === 'com.canhub.cropper.CropImageActivity')) {
23
- app.activity.push({
24
- $: {
25
- 'android:name': 'com.canhub.cropper.CropImageActivity',
26
- 'android:theme': '@style/Base.Theme.AppCompat',
27
- },
28
- });
31
+ if (cameraPermission === false) {
32
+ delete infoPlist.NSCameraUsageDescription;
33
+ } else {
34
+ infoPlist.NSCameraUsageDescription =
35
+ cameraPermission || infoPlist.NSCameraUsageDescription || CAMERA_USAGE;
29
36
  }
30
- return androidManifest;
37
+ if (microphonePermission === false) {
38
+ delete infoPlist.NSMicrophoneUsageDescription;
39
+ } else {
40
+ infoPlist.NSMicrophoneUsageDescription =
41
+ microphonePermission || infoPlist.NSMicrophoneUsageDescription || MICROPHONE_USAGE;
42
+ }
43
+ return infoPlist;
31
44
  }
32
45
 
33
- const withImagePickerManifestActivity: ConfigPlugin = (config) => {
34
- // This plugin has no ability to remove the activity that it adds.
35
- return withAndroidManifest(config, async (config) => {
36
- config.modResults = setImagePickerManifestActivity(config.modResults);
46
+ export const withAndroidImagePickerPermissions: ConfigPlugin<Props | void> = (
47
+ config,
48
+ { cameraPermission, microphonePermission } = {}
49
+ ) => {
50
+ if (microphonePermission !== false) {
51
+ config = AndroidConfig.Permissions.withPermissions(config, ['android.permission.RECORD_AUDIO']);
52
+ }
53
+
54
+ // If the user manually sets any of the permissions to `false`, then we should block the permissions to ensure no
55
+ // package can add them.
56
+ config = AndroidConfig.Permissions.withBlockedPermissions(
57
+ config,
58
+ [
59
+ microphonePermission === false && 'android.permission.RECORD_AUDIO',
60
+ cameraPermission === false && 'android.permission.CAMERA',
61
+ ].filter(Boolean) as string[]
62
+ );
63
+
64
+ // NOTE(EvanBacon): It's unclear if we should block the WRITE_EXTERNAL_STORAGE/READ_EXTERNAL_STORAGE permissions since
65
+ // they're used for many other things besides image picker.
66
+
67
+ return config;
68
+ };
69
+
70
+ const withImagePicker: ConfigPlugin<Props | void> = (
71
+ config,
72
+ { photosPermission, cameraPermission, microphonePermission } = {}
73
+ ) => {
74
+ config = withInfoPlist(config, (config) => {
75
+ config.modResults = setImagePickerInfoPlist(config.modResults, {
76
+ photosPermission,
77
+ cameraPermission,
78
+ microphonePermission,
79
+ });
37
80
  return config;
38
81
  });
39
- };
40
82
 
41
- const withImagePicker: ConfigPlugin<
42
- {
43
- photosPermission?: string;
44
- cameraPermission?: string;
45
- microphonePermission?: string;
46
- } | void
47
- > = (config, { photosPermission, cameraPermission, microphonePermission } = {}) => {
48
- if (!config.ios) config.ios = {};
49
- if (!config.ios.infoPlist) config.ios.infoPlist = {};
50
- config.ios.infoPlist.NSPhotoLibraryUsageDescription =
51
- photosPermission || config.ios.infoPlist.NSPhotoLibraryUsageDescription || READ_PHOTOS_USAGE;
52
- config.ios.infoPlist.NSCameraUsageDescription =
53
- cameraPermission || config.ios.infoPlist.NSCameraUsageDescription || CAMERA_USAGE;
54
- config.ios.infoPlist.NSMicrophoneUsageDescription =
55
- microphonePermission || config.ios.infoPlist.NSMicrophoneUsageDescription || MICROPHONE_USAGE;
83
+ if (microphonePermission !== false) {
84
+ config = AndroidConfig.Permissions.withPermissions(config, ['android.permission.RECORD_AUDIO']);
85
+ }
56
86
 
57
- return withPlugins(config, [
87
+ // If the user manually sets any of the permissions to `false`, then we should block the permissions to ensure no
88
+ // package can add them.
89
+ config = AndroidConfig.Permissions.withBlockedPermissions(
90
+ config,
58
91
  [
59
- AndroidConfig.Permissions.withPermissions,
60
- [
61
- 'android.permission.CAMERA',
62
- 'android.permission.READ_EXTERNAL_STORAGE',
63
- 'android.permission.WRITE_EXTERNAL_STORAGE',
64
- 'android.permission.RECORD_AUDIO',
65
- ],
66
- ],
67
- withImagePickerManifestActivity,
68
- ]);
92
+ microphonePermission === false && 'android.permission.RECORD_AUDIO',
93
+ cameraPermission === false && 'android.permission.CAMERA',
94
+ ].filter(Boolean) as string[]
95
+ );
96
+
97
+ // NOTE(EvanBacon): It's unclear if we should block the WRITE_EXTERNAL_STORAGE/READ_EXTERNAL_STORAGE permissions since
98
+ // they're used for many other things besides image picker.
99
+
100
+ return config;
69
101
  };
70
102
 
71
103
  export default createRunOncePlugin(withImagePicker, pkg.name, pkg.version);