32
32
import java .util .List ;
33
33
import java .util .Map ;
34
34
import java .util .UUID ;
35
+ import java .util .concurrent .ExecutorService ;
36
+ import java .util .concurrent .Executors ;
35
37
36
38
/**
37
39
* A delegate class doing the heavy lifting for the plugin.
@@ -112,6 +114,7 @@ private PendingCallState(
112
114
private final PermissionManager permissionManager ;
113
115
private final FileUriResolver fileUriResolver ;
114
116
private final FileUtils fileUtils ;
117
+ private final ExecutorService executor ;
115
118
private CameraDevice cameraDevice ;
116
119
117
120
interface PermissionManager {
@@ -134,6 +137,7 @@ interface OnPathReadyListener {
134
137
135
138
private Uri pendingCameraMediaUri ;
136
139
private @ Nullable PendingCallState pendingCallState ;
140
+ private final Object pendingCallStateLock = new Object ();
137
141
138
142
public ImagePickerDelegate (
139
143
final Activity activity ,
@@ -185,7 +189,8 @@ public void onScanCompleted(String path, Uri uri) {
185
189
});
186
190
}
187
191
},
188
- new FileUtils ());
192
+ new FileUtils (),
193
+ Executors .newSingleThreadExecutor ());
189
194
}
190
195
191
196
/**
@@ -203,7 +208,8 @@ public void onScanCompleted(String path, Uri uri) {
203
208
final ImagePickerCache cache ,
204
209
final PermissionManager permissionManager ,
205
210
final FileUriResolver fileUriResolver ,
206
- final FileUtils fileUtils ) {
211
+ final FileUtils fileUtils ,
212
+ final ExecutorService executor ) {
207
213
this .activity = activity ;
208
214
this .externalFilesDirectory = externalFilesDirectory ;
209
215
this .imageResizer = imageResizer ;
@@ -216,6 +222,7 @@ public void onScanCompleted(String path, Uri uri) {
216
222
this .fileUriResolver = fileUriResolver ;
217
223
this .fileUtils = fileUtils ;
218
224
this .cache = cache ;
225
+ this .executor = executor ;
219
226
}
220
227
221
228
void setCameraDevice (CameraDevice device ) {
@@ -224,19 +231,25 @@ void setCameraDevice(CameraDevice device) {
224
231
225
232
// Save the state of the image picker so it can be retrieved with `retrieveLostImage`.
226
233
void saveStateBeforeResult () {
227
- if (pendingCallState == null ) {
228
- return ;
234
+ ImageSelectionOptions localImageOptions ;
235
+ synchronized (pendingCallStateLock ) {
236
+ if (pendingCallState == null ) {
237
+ return ;
238
+ }
239
+ localImageOptions = pendingCallState .imageOptions ;
229
240
}
230
241
231
242
cache .saveType (
232
- pendingCallState . imageOptions != null
243
+ localImageOptions != null
233
244
? ImagePickerCache .CacheType .IMAGE
234
245
: ImagePickerCache .CacheType .VIDEO );
235
- if (pendingCallState . imageOptions != null ) {
236
- cache .saveDimensionWithOutputOptions (pendingCallState . imageOptions );
246
+ if (localImageOptions != null ) {
247
+ cache .saveDimensionWithOutputOptions (localImageOptions );
237
248
}
238
- if (pendingCameraMediaUri != null ) {
239
- cache .savePendingCameraMediaUriPath (pendingCameraMediaUri );
249
+
250
+ final Uri localPendingCameraMediaUri = pendingCameraMediaUri ;
251
+ if (localPendingCameraMediaUri != null ) {
252
+ cache .savePendingCameraMediaUriPath (localPendingCameraMediaUri );
240
253
}
241
254
}
242
255
@@ -323,10 +336,16 @@ public void takeVideoWithCamera(
323
336
324
337
private void launchTakeVideoWithCameraIntent () {
325
338
Intent intent = new Intent (MediaStore .ACTION_VIDEO_CAPTURE );
326
- if (pendingCallState != null
327
- && pendingCallState .videoOptions != null
328
- && pendingCallState .videoOptions .getMaxDurationSeconds () != null ) {
329
- int maxSeconds = pendingCallState .videoOptions .getMaxDurationSeconds ().intValue ();
339
+
340
+ VideoSelectionOptions localVideoOptions = null ;
341
+ synchronized (pendingCallStateLock ) {
342
+ if (pendingCallState != null ) {
343
+ localVideoOptions = pendingCallState .videoOptions ;
344
+ }
345
+ }
346
+
347
+ if (localVideoOptions != null && localVideoOptions .getMaxDurationSeconds () != null ) {
348
+ int maxSeconds = localVideoOptions .getMaxDurationSeconds ().intValue ();
330
349
intent .putExtra (MediaStore .EXTRA_DURATION_LIMIT , maxSeconds );
331
350
}
332
351
if (cameraDevice == CameraDevice .FRONT ) {
@@ -537,27 +556,31 @@ public boolean onRequestPermissionsResult(
537
556
}
538
557
539
558
@ Override
540
- public boolean onActivityResult (int requestCode , int resultCode , Intent data ) {
559
+ public boolean onActivityResult (final int requestCode , final int resultCode , final Intent data ) {
560
+ Runnable handlerRunnable ;
561
+
541
562
switch (requestCode ) {
542
563
case REQUEST_CODE_CHOOSE_IMAGE_FROM_GALLERY :
543
- handleChooseImageResult (resultCode , data );
564
+ handlerRunnable = () -> handleChooseImageResult (resultCode , data );
544
565
break ;
545
566
case REQUEST_CODE_CHOOSE_MULTI_IMAGE_FROM_GALLERY :
546
- handleChooseMultiImageResult (resultCode , data );
567
+ handlerRunnable = () -> handleChooseMultiImageResult (resultCode , data );
547
568
break ;
548
569
case REQUEST_CODE_TAKE_IMAGE_WITH_CAMERA :
549
- handleCaptureImageResult (resultCode );
570
+ handlerRunnable = () -> handleCaptureImageResult (resultCode );
550
571
break ;
551
572
case REQUEST_CODE_CHOOSE_VIDEO_FROM_GALLERY :
552
- handleChooseVideoResult (resultCode , data );
573
+ handlerRunnable = () -> handleChooseVideoResult (resultCode , data );
553
574
break ;
554
575
case REQUEST_CODE_TAKE_VIDEO_WITH_CAMERA :
555
- handleCaptureVideoResult (resultCode );
576
+ handlerRunnable = () -> handleCaptureVideoResult (resultCode );
556
577
break ;
557
578
default :
558
579
return false ;
559
580
}
560
581
582
+ executor .execute (handlerRunnable );
583
+
561
584
return true ;
562
585
}
563
586
@@ -603,9 +626,11 @@ private void handleChooseVideoResult(int resultCode, Intent data) {
603
626
604
627
private void handleCaptureImageResult (int resultCode ) {
605
628
if (resultCode == Activity .RESULT_OK ) {
629
+ final Uri localPendingCameraMediaUri = pendingCameraMediaUri ;
630
+
606
631
fileUriResolver .getFullImagePath (
607
- pendingCameraMediaUri != null
608
- ? pendingCameraMediaUri
632
+ localPendingCameraMediaUri != null
633
+ ? localPendingCameraMediaUri
609
634
: Uri .parse (cache .retrievePendingCameraMediaUriPath ()),
610
635
new OnPathReadyListener () {
611
636
@ Override
@@ -622,9 +647,10 @@ public void onPathReady(String path) {
622
647
623
648
private void handleCaptureVideoResult (int resultCode ) {
624
649
if (resultCode == Activity .RESULT_OK ) {
650
+ final Uri localPendingCameraMediaUrl = pendingCameraMediaUri ;
625
651
fileUriResolver .getFullImagePath (
626
- pendingCameraMediaUri != null
627
- ? pendingCameraMediaUri
652
+ localPendingCameraMediaUrl != null
653
+ ? localPendingCameraMediaUrl
628
654
: Uri .parse (cache .retrievePendingCameraMediaUriPath ()),
629
655
new OnPathReadyListener () {
630
656
@ Override
@@ -641,10 +667,17 @@ public void onPathReady(String path) {
641
667
642
668
private void handleMultiImageResult (
643
669
ArrayList <String > paths , boolean shouldDeleteOriginalIfScaled ) {
644
- if (pendingCallState != null && pendingCallState .imageOptions != null ) {
670
+ ImageSelectionOptions localImageOptions = null ;
671
+ synchronized (pendingCallStateLock ) {
672
+ if (pendingCallState != null ) {
673
+ localImageOptions = pendingCallState .imageOptions ;
674
+ }
675
+ }
676
+
677
+ if (localImageOptions != null ) {
645
678
ArrayList <String > finalPath = new ArrayList <>();
646
679
for (int i = 0 ; i < paths .size (); i ++) {
647
- String finalImagePath = getResizedImagePath (paths .get (i ), pendingCallState . imageOptions );
680
+ String finalImagePath = getResizedImagePath (paths .get (i ), localImageOptions );
648
681
649
682
//delete original file if scaled
650
683
if (finalImagePath != null
@@ -661,8 +694,15 @@ private void handleMultiImageResult(
661
694
}
662
695
663
696
private void handleImageResult (String path , boolean shouldDeleteOriginalIfScaled ) {
664
- if (pendingCallState != null && pendingCallState .imageOptions != null ) {
665
- String finalImagePath = getResizedImagePath (path , pendingCallState .imageOptions );
697
+ ImageSelectionOptions localImageOptions = null ;
698
+ synchronized (pendingCallStateLock ) {
699
+ if (pendingCallState != null ) {
700
+ localImageOptions = pendingCallState .imageOptions ;
701
+ }
702
+ }
703
+
704
+ if (localImageOptions != null ) {
705
+ String finalImagePath = getResizedImagePath (path , localImageOptions );
666
706
//delete original file if scaled
667
707
if (finalImagePath != null && !finalImagePath .equals (path ) && shouldDeleteOriginalIfScaled ) {
668
708
new File (path ).delete ();
@@ -689,12 +729,13 @@ private boolean setPendingOptionsAndResult(
689
729
@ Nullable ImageSelectionOptions imageOptions ,
690
730
@ Nullable VideoSelectionOptions videoOptions ,
691
731
@ NonNull Messages .Result <List <String >> result ) {
692
- if (pendingCallState != null ) {
693
- return false ;
732
+ synchronized (pendingCallStateLock ) {
733
+ if (pendingCallState != null ) {
734
+ return false ;
735
+ }
736
+ pendingCallState = new PendingCallState (imageOptions , videoOptions , result );
694
737
}
695
738
696
- pendingCallState = new PendingCallState (imageOptions , videoOptions , result );
697
-
698
739
// Clean up cache if a new image picker is launched.
699
740
cache .clear ();
700
741
@@ -710,37 +751,59 @@ private void finishWithSuccess(@Nullable String imagePath) {
710
751
if (imagePath != null ) {
711
752
pathList .add (imagePath );
712
753
}
713
- if (pendingCallState == null ) {
754
+
755
+ Messages .Result <List <String >> localResult = null ;
756
+ synchronized (pendingCallStateLock ) {
757
+ if (pendingCallState != null ) {
758
+ localResult = pendingCallState .result ;
759
+ }
760
+ pendingCallState = null ;
761
+ }
762
+
763
+ if (localResult == null ) {
714
764
// Only save data for later retrieval if something was actually selected.
715
765
if (!pathList .isEmpty ()) {
716
766
cache .saveResult (pathList , null , null );
717
767
}
718
- return ;
768
+ } else {
769
+ localResult .success (pathList );
719
770
}
720
- pendingCallState .result .success (pathList );
721
- pendingCallState = null ;
722
771
}
723
772
724
773
private void finishWithListSuccess (ArrayList <String > imagePaths ) {
725
- if (pendingCallState == null ) {
774
+ Messages .Result <List <String >> localResult = null ;
775
+ synchronized (pendingCallStateLock ) {
776
+ if (pendingCallState != null ) {
777
+ localResult = pendingCallState .result ;
778
+ }
779
+ pendingCallState = null ;
780
+ }
781
+
782
+ if (localResult == null ) {
726
783
cache .saveResult (imagePaths , null , null );
727
- return ;
784
+ } else {
785
+ localResult .success (imagePaths );
728
786
}
729
- pendingCallState .result .success (imagePaths );
730
- pendingCallState = null ;
731
787
}
732
788
733
789
private void finishWithAlreadyActiveError (Messages .Result <List <String >> result ) {
734
790
result .error (new FlutterError ("already_active" , "Image picker is already active" , null ));
735
791
}
736
792
737
793
private void finishWithError (String errorCode , String errorMessage ) {
738
- if (pendingCallState == null ) {
794
+ Messages .Result <List <String >> localResult = null ;
795
+ synchronized (pendingCallStateLock ) {
796
+ if (pendingCallState != null ) {
797
+ localResult = pendingCallState .result ;
798
+ }
799
+ pendingCallState = null ;
800
+ }
801
+
802
+ if (localResult == null ) {
739
803
cache .saveResult (null , errorCode , errorMessage );
740
- return ;
804
+ } else {
805
+ localResult .error (new FlutterError (errorCode , errorMessage , null ));
741
806
}
742
- pendingCallState .result .error (new FlutterError (errorCode , errorMessage , null ));
743
- pendingCallState = null ;
744
807
}
745
808
746
809
private void useFrontCamera (Intent intent ) {
0 commit comments