@connectedbits/react-native-image-picker 2.4.16-rc2 → 2.4.17

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.
@@ -6,7 +6,6 @@ import android.content.ActivityNotFoundException;
6
6
  import android.content.Context;
7
7
  import android.content.DialogInterface;
8
8
  import android.content.Intent;
9
- import android.content.pm.ResolveInfo;
10
9
  import android.graphics.Bitmap;
11
10
  import android.graphics.BitmapFactory;
12
11
  import android.net.Uri;
@@ -14,7 +13,10 @@ import android.os.Build;
14
13
  import android.os.Handler;
15
14
  import android.provider.MediaStore;
16
15
  import android.provider.Settings;
16
+ import androidx.activity.result.PickVisualMediaRequest;
17
+ import androidx.activity.result.contract.ActivityResultContracts;
17
18
  import androidx.annotation.NonNull;
19
+ import androidx.annotation.Nullable;
18
20
  import androidx.annotation.StyleRes;
19
21
  import androidx.core.app.ActivityCompat;
20
22
  import androidx.appcompat.app.AlertDialog;
@@ -47,7 +49,6 @@ import java.io.IOException;
47
49
  import java.io.InputStream;
48
50
  import java.io.UnsupportedEncodingException;
49
51
  import java.lang.ref.WeakReference;
50
- import java.util.List;
51
52
 
52
53
  import com.facebook.react.modules.core.PermissionListener;
53
54
  import com.facebook.react.modules.core.PermissionAwareActivity;
@@ -69,6 +70,8 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
69
70
  public static final int REQUEST_LAUNCH_VIDEO_LIBRARY = 13003;
70
71
  public static final int REQUEST_LAUNCH_VIDEO_CAPTURE = 13004;
71
72
  public static final int REQUEST_PERMISSIONS_FOR_CAMERA = 14001;
73
+ // The system Photo Picker needs no storage permission. We only (opportunistically) ask for
74
+ // ACCESS_MEDIA_LOCATION so that EXIF GPS can be read when the provider is willing to hand it back.
72
75
  public static final int REQUEST_PERMISSIONS_FOR_LIBRARY = 14002;
73
76
 
74
77
  private final ReactApplicationContext reactContext;
@@ -91,6 +94,7 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
91
94
  private int videoDurationLimit = 0;
92
95
 
93
96
  private ResponseHelper responseHelper = new ResponseHelper();
97
+ private boolean mediaLocationRequested = false;
94
98
  private PermissionListener listener = new PermissionListener()
95
99
  {
96
100
  public boolean onRequestPermissionsResult(final int requestCode,
@@ -109,24 +113,26 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
109
113
  return false;
110
114
  }
111
115
 
112
- if (!permissionsGranted)
113
- {
114
- responseHelper.invokeError(permissionRequestCallback, "Permissions weren't granted");
115
- return false;
116
- }
117
-
118
116
  switch (requestCode)
119
117
  {
120
118
  case REQUEST_PERMISSIONS_FOR_CAMERA:
119
+ if (!permissionsGranted)
120
+ {
121
+ responseHelper.invokeError(permissionRequestCallback, "Permissions weren't granted");
122
+ callback = null;
123
+ return false;
124
+ }
121
125
  launchCamera(options, permissionRequestCallback);
122
- break;
126
+ return true;
123
127
 
124
128
  case REQUEST_PERMISSIONS_FOR_LIBRARY:
129
+ // Open the picker whether or not media location was granted; without it we simply
130
+ // won't get EXIF GPS back.
131
+ mediaLocationRequested = true;
125
132
  launchImageLibrary(options, permissionRequestCallback);
126
- break;
127
-
133
+ return true;
128
134
  }
129
- return true;
135
+ return false;
130
136
  }
131
137
  };
132
138
 
@@ -152,23 +158,22 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
152
158
 
153
159
  @ReactMethod
154
160
  public void showImagePicker(final ReadableMap options, final Callback callback) {
161
+ final ImagePickerModule self = this;
162
+ final Activity currentActivity = getReactApplicationContext().getCurrentActivity();
155
163
  Handler handler = new Handler(this.reactContext.getMainLooper());
164
+ this.callback = callback;
165
+ this.options = options;
166
+ if (currentActivity == null)
167
+ {
168
+ responseHelper.invokeError(callback, "can't find current Activity");
169
+ return;
170
+ }
171
+ imageConfig = new ImageConfig(0, 0, 100, 0, false);
156
172
  handler.post(new Runnable() {
157
173
  @Override
158
174
  public void run() {
159
- Activity currentActivity = getCurrentActivity();
160
-
161
- if (currentActivity == null)
162
- {
163
- responseHelper.invokeError(callback, "can't find current Activity");
164
- return;
165
- }
166
-
167
- this.callback = callback;
168
- this.options = options;
169
- imageConfig = new ImageConfig(0, 0, 100, 0, false);
170
175
 
171
- final AlertDialog dialog = UI.chooseDialog(this, options, new UI.OnAction()
176
+ final AlertDialog dialog = UI.chooseDialog(self, options, new UI.OnAction()
172
177
  {
173
178
  @Override
174
179
  public void onTakePhoto(@NonNull final ImagePickerModule module)
@@ -233,34 +238,35 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
233
238
  @ReactMethod
234
239
  public void launchCamera(final ReadableMap options, final Callback callback)
235
240
  {
236
- Handler handler = new Handler(this.reactContext.getMainLooper());
237
- handler.post(new Runnable() {
238
- @Override
239
- public void run() {
240
- permissionRequestCallback = callback;
241
+ final ImagePickerModule self = this;
242
+ Handler handler = new Handler(this.getContext().getMainLooper());
243
+ permissionRequestCallback = callback;
241
244
 
242
- if (!isCameraAvailable())
243
- {
244
- responseHelper.invokeError(callback, "Camera not available");
245
- return;
246
- }
245
+ if (!isCameraAvailable())
246
+ {
247
+ responseHelper.invokeError(callback, "Camera not available");
248
+ return;
249
+ }
247
250
 
248
- final Activity currentActivity = getCurrentActivity();
249
- if (currentActivity == null)
250
- {
251
- responseHelper.invokeError(callback, "can't find current Activity");
252
- return;
253
- }
251
+ final Activity currentActivity = getReactApplicationContext().getCurrentActivity();
252
+ if (currentActivity == null)
253
+ {
254
+ responseHelper.invokeError(callback, "can't find current Activity");
255
+ return;
256
+ }
254
257
 
255
- this.callback = callback;
256
- this.options = options;
258
+ this.callback = callback;
259
+ this.options = options;
257
260
 
261
+ handler.post(new Runnable() {
262
+ @Override
263
+ public void run() {
258
264
  if (!permissionsCheck(currentActivity, callback, REQUEST_PERMISSIONS_FOR_CAMERA))
259
265
  {
260
266
  return;
261
267
  }
262
268
 
263
- parseOptions(this.options);
269
+ parseOptions(options);
264
270
 
265
271
  int requestCode;
266
272
  Intent cameraIntent;
@@ -280,10 +286,10 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
280
286
  requestCode = REQUEST_LAUNCH_IMAGE_CAPTURE;
281
287
  cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
282
288
 
283
- final File original = createNewFile(reactContext, this.options, false);
289
+ final File original = createNewFile(self.getContext(), options, false);
284
290
 
285
291
  if (original != null) {
286
- cameraCaptureURI = RealPathUtil.compatUriFromFile(reactContext, original);
292
+ cameraCaptureURI = RealPathUtil.compatUriFromFile(self.getContext(), original);
287
293
  }else {
288
294
  responseHelper.invokeError(callback, "Couldn't get file path for photo");
289
295
  return;
@@ -296,16 +302,7 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
296
302
  cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, cameraCaptureURI);
297
303
  }
298
304
 
299
- // Workaround for Android bug.
300
- // grantUriPermission also needed for KITKAT,
301
- // see https://code.google.com/p/android/issues/detail?id=76683
302
- if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) {
303
- List<ResolveInfo> resInfoList = reactContext.getPackageManager().queryIntentActivities(cameraIntent, PackageManager.MATCH_DEFAULT_ONLY);
304
- for (ResolveInfo resolveInfo : resInfoList) {
305
- String packageName = resolveInfo.activityInfo.packageName;
306
- reactContext.grantUriPermission(packageName, cameraCaptureURI, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
307
- }
308
- }
305
+ cameraIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
309
306
 
310
307
  try
311
308
  {
@@ -328,62 +325,55 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
328
325
  @ReactMethod
329
326
  public void launchImageLibrary(final ReadableMap options, final Callback callback)
330
327
  {
331
- Handler handler = new Handler(this.reactContext.getMainLooper());
332
- handler.post(new Runnable() {
333
- @Override
334
- public void run(){
335
- permissionRequestCallback = callback;
328
+ permissionRequestCallback = callback;
336
329
 
337
- final Activity currentActivity = getCurrentActivity();
338
- if (currentActivity == null) {
339
- responseHelper.invokeError(callback, "can't find current Activity");
340
- return;
341
- }
330
+ final Activity currentActivity = getReactApplicationContext().getCurrentActivity();
331
+ if (currentActivity == null) {
332
+ responseHelper.invokeError(callback, "can't find current Activity");
333
+ return;
334
+ }
342
335
 
343
- this.callback = callback;
344
- this.options = options;
336
+ this.callback = callback;
337
+ this.options = options;
345
338
 
346
- if (!permissionsCheck(currentActivity, callback, REQUEST_PERMISSIONS_FOR_LIBRARY))
339
+ Handler handler = new Handler(this.getContext().getMainLooper());
340
+ handler.post(new Runnable() {
341
+ @Override
342
+ public void run(){
343
+ if (shouldRequestMediaLocation(currentActivity))
347
344
  {
345
+ requestPermissions(currentActivity, new String[]{Manifest.permission.ACCESS_MEDIA_LOCATION}, REQUEST_PERMISSIONS_FOR_LIBRARY);
348
346
  return;
349
347
  }
348
+ mediaLocationRequested = false;
350
349
 
351
- parseOptions(this.options);
350
+ parseOptions(options);
352
351
 
353
- int requestCode;
354
- Intent libraryIntent;
352
+ // System Photo Picker (Android 13+, or back-ported via Google Play services). androidx falls
353
+ // back to ACTION_OPEN_DOCUMENT on devices where neither is available.
354
+ final ActivityResultContracts.PickVisualMedia.VisualMediaType mediaType;
355
355
  if (pickVideo)
356
356
  {
357
- requestCode = REQUEST_LAUNCH_VIDEO_LIBRARY;
358
- libraryIntent = new Intent(Intent.ACTION_GET_CONTENT);
359
- libraryIntent.setType("video/*");
360
- libraryIntent.putExtra(Intent.EXTRA_LOCAL_ONLY, true);
361
- libraryIntent.putExtra(Intent.CATEGORY_OPENABLE, true);
357
+ mediaType = ActivityResultContracts.PickVisualMedia.VideoOnly.INSTANCE;
358
+ }
359
+ else if (pickBoth)
360
+ {
361
+ mediaType = ActivityResultContracts.PickVisualMedia.ImageAndVideo.INSTANCE;
362
362
  }
363
363
  else
364
364
  {
365
- requestCode = REQUEST_LAUNCH_IMAGE_LIBRARY;
366
- libraryIntent = new Intent(Intent.ACTION_GET_CONTENT);
367
- libraryIntent.putExtra(Intent.EXTRA_LOCAL_ONLY, true);
368
- libraryIntent.putExtra(Intent.CATEGORY_OPENABLE, true);
369
-
370
- if (pickBoth)
371
- {
372
- libraryIntent.setType("image/* video/*");
373
- } else {
374
- libraryIntent.setType("image/*");
375
- }
365
+ mediaType = ActivityResultContracts.PickVisualMedia.ImageOnly.INSTANCE;
376
366
  }
367
+ final PickVisualMediaRequest request = new PickVisualMediaRequest.Builder()
368
+ .setMediaType(mediaType)
369
+ .build();
370
+ final Intent libraryIntent = new ActivityResultContracts.PickVisualMedia()
371
+ .createIntent(currentActivity, request);
372
+ final int requestCode = pickVideo ? REQUEST_LAUNCH_VIDEO_LIBRARY : REQUEST_LAUNCH_IMAGE_LIBRARY;
377
373
 
378
374
  try
379
375
  {
380
- String chooseWhichLibraryTitle = null;
381
- if (ReadableMapUtils.hasAndNotEmptyString(options, "chooseWhichLibraryTitle"))
382
- {
383
- chooseWhichLibraryTitle = options.getString("chooseWhichLibraryTitle");
384
- }
385
-
386
- currentActivity.startActivityForResult(Intent.createChooser(libraryIntent, chooseWhichLibraryTitle), requestCode);
376
+ currentActivity.startActivityForResult(libraryIntent, requestCode);
387
377
  }
388
378
  catch (ActivityNotFoundException e)
389
379
  {
@@ -394,13 +384,28 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
394
384
  });
395
385
  }
396
386
 
387
+ /**
388
+ * Ask for ACCESS_MEDIA_LOCATION once per library launch when it isn't granted yet. If the user has
389
+ * already denied it permanently the request returns immediately without UI.
390
+ */
391
+ private boolean shouldRequestMediaLocation(@NonNull final Activity activity)
392
+ {
393
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q || mediaLocationRequested)
394
+ {
395
+ return false;
396
+ }
397
+ return ActivityCompat.checkSelfPermission(activity, Manifest.permission.ACCESS_MEDIA_LOCATION)
398
+ != PackageManager.PERMISSION_GRANTED;
399
+ }
400
+
397
401
  @Override
398
- public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) {
399
- Handler handler = new Handler(this.reactContext.getMainLooper());
402
+ public void onActivityResult(Activity activity, final int requestCode, final int resultCode, final Intent data) {
403
+ final ImagePickerModule self = this;
404
+ Handler handler = new Handler(this.getContext().getMainLooper());
400
405
  handler.post(new Runnable() {
401
406
  @Override
402
407
  public void run() {
403
- if (passResult(requestCode))
408
+ if (self.passResult(requestCode))
404
409
  {
405
410
  return;
406
411
  }
@@ -416,6 +421,7 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
416
421
  }
417
422
 
418
423
  Uri uri = null;
424
+ boolean fromLibrary = false;
419
425
  switch (requestCode)
420
426
  {
421
427
  case REQUEST_LAUNCH_IMAGE_CAPTURE:
@@ -423,35 +429,44 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
423
429
  break;
424
430
 
425
431
  case REQUEST_LAUNCH_IMAGE_LIBRARY:
426
- uri = data.getData();
427
- if(uri == null){
428
- responseHelper.putString("error", "Could not read photo");
432
+ uri = data != null ? data.getData() : null;
433
+ if (uri == null) {
434
+ responseHelper.invokeError(callback, "Could not read photo");
435
+ callback = null;
436
+ return;
437
+ }
438
+ // mediaType "mixed" can hand back a video from the image request code.
439
+ final String mimeType = self.getContext().getContentResolver().getType(uri);
440
+ if (mimeType != null && mimeType.startsWith("video/")) {
441
+ responseHelper.putString("uri", uri.toString());
429
442
  responseHelper.invokeResponse(callback);
430
443
  callback = null;
431
444
  return;
432
445
  }
446
+ fromLibrary = true;
433
447
  break;
434
448
 
435
449
  case REQUEST_LAUNCH_VIDEO_LIBRARY:
436
- responseHelper.putString("uri", data.getData().toString());
437
- responseHelper.invokeResponse(callback);
438
- callback = null;
439
- return;
440
-
441
450
  case REQUEST_LAUNCH_VIDEO_CAPTURE:
451
+ if (data == null || data.getData() == null) {
452
+ responseHelper.invokeError(callback, "Could not read video");
453
+ callback = null;
454
+ return;
455
+ }
442
456
  responseHelper.putString("uri", data.getData().toString());
443
457
  responseHelper.invokeResponse(callback);
444
458
  callback = null;
445
459
  return;
446
460
  }
447
461
 
462
+ // EXIF (location/timestamp/orientation). For library picks, ask for the unredacted original
463
+ // so GPS survives when ACCESS_MEDIA_LOCATION is granted and the provider honours it.
448
464
  ReadExifResult result = null;
449
- try {
450
- InputStream imageInputStream = reactContext.getContentResolver().openInputStream(uri);
451
- if(imageInputStream != null){
465
+ try (InputStream imageInputStream = openExifInputStream(uri, fromLibrary)) {
466
+ if (imageInputStream != null) {
452
467
  result = readExifInterface(imageInputStream, responseHelper);
453
468
  }
454
- } catch(IOException ex){
469
+ } catch (IOException ex) {
455
470
  responseHelper.invokeError(callback, ex.getMessage());
456
471
  callback = null;
457
472
  return;
@@ -470,31 +485,26 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
470
485
 
471
486
  BitmapFactory.Options options = new BitmapFactory.Options();
472
487
  options.inJustDecodeBounds = true;
473
- Bitmap imageBitmap = null;
474
- try {
475
- InputStream imageInputStream = reactContext.getContentResolver().openInputStream(uri);
476
- imageBitmap = BitmapFactory.decodeStream(imageInputStream, null, options);
477
- } catch(FileNotFoundException ex){
488
+ try (InputStream imageInputStream = self.getContext().getContentResolver().openInputStream(uri)) {
489
+ BitmapFactory.decodeStream(imageInputStream, null, options);
490
+ } catch (IOException ex) {
478
491
  responseHelper.invokeError(callback, "Could not find file");
479
492
  callback = null;
480
493
  return;
481
494
  }
482
495
 
483
-
484
496
  int initialWidth = options.outWidth;
485
497
  int initialHeight = options.outHeight;
486
498
 
487
-
488
499
  // don't create a new file if contraint are respected
489
500
  if (imageConfig.useOriginal(initialWidth, initialHeight, result.currentRotation))
490
501
  {
491
502
  responseHelper.putInt("width", initialWidth);
492
503
  responseHelper.putInt("height", initialHeight);
493
504
 
494
- try {
495
- InputStream imageInputStream = reactContext.getContentResolver().openInputStream(uri);
505
+ try (InputStream imageInputStream = self.getContext().getContentResolver().openInputStream(uri)) {
496
506
  updatedResultResponse(imageInputStream);
497
- } catch(FileNotFoundException ex){
507
+ } catch (IOException ex) {
498
508
  responseHelper.invokeError(callback, "Could not find file");
499
509
  callback = null;
500
510
  return;
@@ -504,10 +514,9 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
504
514
  {
505
515
  Bitmap resizedImage = null;
506
516
  ImageConfig rotatedImageConfig = imageConfig.withRotation(result.currentRotation);
507
- try {
508
- InputStream imageInputStream = reactContext.getContentResolver().openInputStream(uri);
517
+ try (InputStream imageInputStream = self.getContext().getContentResolver().openInputStream(uri)) {
509
518
  resizedImage = getResizedImage(imageInputStream, rotatedImageConfig, initialWidth, initialHeight);
510
- } catch(FileNotFoundException ex){
519
+ } catch (IOException ex) {
511
520
  responseHelper.invokeError(callback, "Could not find file");
512
521
  callback = null;
513
522
  return;
@@ -527,22 +536,38 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
527
536
  ByteArrayInputStream bs = new ByteArrayInputStream(bitmapdata);
528
537
  updatedResultResponse(bs);
529
538
  resizedImage.recycle();
530
- resizedImage = null;
531
539
  }
532
540
  }
533
- if(imageBitmap != null){
534
- imageBitmap.recycle();
535
- imageBitmap = null;
536
- }
537
-
538
541
 
539
542
  responseHelper.invokeResponse(callback);
540
- callback = null;
541
- this.options = null;
543
+ self.callback = null;
544
+ self.options = null;
542
545
  }
543
546
  });
544
547
  }
545
548
 
549
+ /**
550
+ * Open a stream for EXIF parsing. On Android 10+ location tags are redacted unless we hold
551
+ * ACCESS_MEDIA_LOCATION and ask for the original via MediaStore.setRequireOriginal. Providers that
552
+ * don't support that (e.g. some Photo Picker configurations) throw, in which case we fall back to the
553
+ * plain (redacted) stream.
554
+ */
555
+ private @Nullable InputStream openExifInputStream(@NonNull final Uri uri, final boolean fromLibrary) throws FileNotFoundException
556
+ {
557
+ if (fromLibrary && Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
558
+ {
559
+ try
560
+ {
561
+ return getContext().getContentResolver().openInputStream(MediaStore.setRequireOriginal(uri));
562
+ }
563
+ catch (UnsupportedOperationException | SecurityException | IllegalArgumentException | FileNotFoundException e)
564
+ {
565
+ // fall through to the plain uri
566
+ }
567
+ }
568
+ return getContext().getContentResolver().openInputStream(uri);
569
+ }
570
+
546
571
  public void invokeCustomButton(@NonNull final String action)
547
572
  {
548
573
  responseHelper.invokeCustomButton(this.callback, action);
@@ -563,7 +588,7 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
563
588
 
564
589
  public @NonNull Activity getActivity()
565
590
  {
566
- return getCurrentActivity();
591
+ return getReactApplicationContext().getCurrentActivity();
567
592
  }
568
593
 
569
594
 
@@ -583,130 +608,89 @@ public class ImagePickerModule extends ReactContextBaseJavaModule
583
608
  @NonNull final Callback callback,
584
609
  @NonNull final int requestCode)
585
610
  {
586
- int writePermission = 0;
587
- int libraryPermission = 0;
588
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
589
- writePermission = ActivityCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE);
590
- libraryPermission = writePermission;
591
- } else {
592
- writePermission = PackageManager.PERMISSION_GRANTED;
593
- libraryPermission = ActivityCompat.checkSelfPermission(activity, Manifest.permission.READ_MEDIA_IMAGES);
594
- }
595
-
596
- final int cameraPermission = ActivityCompat
597
- .checkSelfPermission(activity, Manifest.permission.CAMERA);
598
-
599
- boolean permissionsGranted = false;
600
-
601
- switch (requestCode) {
602
- case REQUEST_PERMISSIONS_FOR_LIBRARY:
603
- permissionsGranted = libraryPermission == PackageManager.PERMISSION_GRANTED;
604
- break;
605
- case REQUEST_PERMISSIONS_FOR_CAMERA:
606
- permissionsGranted = cameraPermission == PackageManager.PERMISSION_GRANTED && writePermission == PackageManager.PERMISSION_GRANTED;
607
- break;
611
+ // Only the camera needs a runtime permission. Captures are written to app-specific storage
612
+ // (getExternalFilesDir) and the library uses the system Photo Picker, so no storage permission
613
+ // is required on any supported API level.
614
+ final boolean permissionsGranted = ActivityCompat
615
+ .checkSelfPermission(activity, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED;
616
+ if (permissionsGranted)
617
+ {
618
+ return true;
608
619
  }
609
620
 
610
- if (!permissionsGranted)
621
+ if (ActivityCompat.shouldShowRequestPermissionRationale(activity, Manifest.permission.CAMERA))
611
622
  {
612
- Boolean dontAskAgain = false;
613
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
614
- dontAskAgain = ActivityCompat.shouldShowRequestPermissionRationale(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE) && ActivityCompat.shouldShowRequestPermissionRationale(activity, Manifest.permission.CAMERA);
615
- } else {
616
- dontAskAgain = ActivityCompat.shouldShowRequestPermissionRationale(activity, Manifest.permission.READ_MEDIA_IMAGES) && ActivityCompat.shouldShowRequestPermissionRationale(activity, Manifest.permission.CAMERA);
617
- }
618
-
619
- if (dontAskAgain)
620
- {
621
- final AlertDialog dialog = PermissionUtils
622
- .explainingDialog(this, options, new PermissionUtils.OnExplainingPermissionCallback()
623
+ final AlertDialog dialog = PermissionUtils
624
+ .explainingDialog(this, options, new PermissionUtils.OnExplainingPermissionCallback()
625
+ {
626
+ @Override
627
+ public void onCancel(WeakReference<ImagePickerModule> moduleInstance,
628
+ DialogInterface dialogInterface)
623
629
  {
624
- @Override
625
- public void onCancel(WeakReference<ImagePickerModule> moduleInstance,
626
- DialogInterface dialogInterface)
630
+ final ImagePickerModule module = moduleInstance.get();
631
+ if (module == null)
627
632
  {
628
- final ImagePickerModule module = moduleInstance.get();
629
- if (module == null)
630
- {
631
- return;
632
- }
633
- module.doOnCancel();
633
+ return;
634
634
  }
635
+ module.doOnCancel();
636
+ }
635
637
 
636
- @Override
637
- public void onReTry(WeakReference<ImagePickerModule> moduleInstance,
638
- DialogInterface dialogInterface)
638
+ @Override
639
+ public void onReTry(WeakReference<ImagePickerModule> moduleInstance,
640
+ DialogInterface dialogInterface)
641
+ {
642
+ final ImagePickerModule module = moduleInstance.get();
643
+ if (module == null)
639
644
  {
640
- final ImagePickerModule module = moduleInstance.get();
641
- if (module == null)
642
- {
643
- return;
644
- }
645
- Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
646
- Uri uri = Uri.fromParts("package", module.getContext().getPackageName(), null);
647
- intent.setData(uri);
648
- final Activity innerActivity = module.getActivity();
649
- if (innerActivity == null)
650
- {
651
- return;
652
- }
653
- innerActivity.startActivityForResult(intent, 1);
645
+ return;
654
646
  }
655
- });
656
- if (dialog != null) {
657
- dialog.show();
658
- }
659
- return false;
647
+ Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
648
+ Uri uri = Uri.fromParts("package", module.getContext().getPackageName(), null);
649
+ intent.setData(uri);
650
+ final Activity innerActivity = module.getActivity();
651
+ if (innerActivity == null)
652
+ {
653
+ return;
654
+ }
655
+ innerActivity.startActivityForResult(intent, 1);
656
+ }
657
+ });
658
+ if (dialog != null) {
659
+ dialog.show();
660
660
  }
661
- else
662
- {
663
- String[] PERMISSIONS;
664
- switch (requestCode) {
665
- case REQUEST_PERMISSIONS_FOR_LIBRARY:
666
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
667
- PERMISSIONS = new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE};
668
- } else {
669
- PERMISSIONS = new String[]{Manifest.permission.READ_MEDIA_IMAGES};
670
- }
671
- break;
672
- case REQUEST_PERMISSIONS_FOR_CAMERA:
673
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
674
- PERMISSIONS = new String[]{Manifest.permission.CAMERA,Manifest.permission.WRITE_EXTERNAL_STORAGE};
675
- } else {
676
- PERMISSIONS = new String[]{Manifest.permission.CAMERA};
677
- }
678
- break;
679
- default:
680
- PERMISSIONS = new String[]{};
681
- break;
682
- }
661
+ return false;
662
+ }
683
663
 
684
- if (activity instanceof ReactActivity)
685
- {
686
- ((ReactActivity) activity).requestPermissions(PERMISSIONS, requestCode, listener);
687
- }
688
- else if (activity instanceof PermissionAwareActivity) {
689
- ((PermissionAwareActivity) activity).requestPermissions(PERMISSIONS, requestCode, listener);
690
- }
691
- else if (activity instanceof OnImagePickerPermissionsCallback)
692
- {
693
- ((OnImagePickerPermissionsCallback) activity).setPermissionListener(listener);
694
- ActivityCompat.requestPermissions(activity, PERMISSIONS, requestCode);
695
- }
696
- else
697
- {
698
- final String errorDescription = new StringBuilder(activity.getClass().getSimpleName())
699
- .append(" must implement ")
700
- .append(OnImagePickerPermissionsCallback.class.getSimpleName())
701
- .append(" or ")
702
- .append(PermissionAwareActivity.class.getSimpleName())
703
- .toString();
704
- throw new UnsupportedOperationException(errorDescription);
705
- }
706
- return false;
707
- }
664
+ requestPermissions(activity, new String[]{Manifest.permission.CAMERA}, requestCode);
665
+ return false;
666
+ }
667
+
668
+ private void requestPermissions(@NonNull final Activity activity,
669
+ @NonNull final String[] permissions,
670
+ final int requestCode)
671
+ {
672
+ if (activity instanceof ReactActivity)
673
+ {
674
+ ((ReactActivity) activity).requestPermissions(permissions, requestCode, listener);
675
+ }
676
+ else if (activity instanceof PermissionAwareActivity) {
677
+ ((PermissionAwareActivity) activity).requestPermissions(permissions, requestCode, listener);
678
+ }
679
+ else if (activity instanceof OnImagePickerPermissionsCallback)
680
+ {
681
+ ((OnImagePickerPermissionsCallback) activity).setPermissionListener(listener);
682
+ ActivityCompat.requestPermissions(activity, permissions, requestCode);
683
+ }
684
+ else
685
+ {
686
+ final String errorDescription = new StringBuilder(activity.getClass().getSimpleName())
687
+ .append(" must implement ")
688
+ .append(OnImagePickerPermissionsCallback.class.getSimpleName())
689
+ .append(" or ")
690
+ .append(PermissionAwareActivity.class.getSimpleName())
691
+ .toString();
692
+ throw new UnsupportedOperationException(errorDescription);
708
693
  }
709
- return true;
710
694
  }
711
695
 
712
696
  private boolean isCameraAvailable() {