12
12
import android .graphics .SurfaceTexture ;
13
13
import android .hardware .camera2 .CameraAccessException ;
14
14
import android .hardware .camera2 .CameraCaptureSession ;
15
+ import android .hardware .camera2 .CameraCaptureSession .CaptureCallback ;
15
16
import android .hardware .camera2 .CameraCharacteristics ;
16
17
import android .hardware .camera2 .CameraDevice ;
17
18
import android .hardware .camera2 .CameraManager ;
32
33
import android .os .Build .VERSION_CODES ;
33
34
import android .util .Range ;
34
35
import android .util .Rational ;
36
+ import android .os .Handler ;
37
+ import android .os .Looper ;
35
38
import android .util .Size ;
36
39
import android .view .OrientationEventListener ;
37
40
import android .view .Surface ;
41
44
import io .flutter .plugins .camera .media .MediaRecorderBuilder ;
42
45
import io .flutter .plugins .camera .types .ExposureMode ;
43
46
import io .flutter .plugins .camera .types .IsoMode ;
47
+ import io .flutter .plugins .camera .PictureCaptureRequest .State ;
44
48
import io .flutter .plugins .camera .types .WbMode ;
45
49
import io .flutter .plugins .camera .types .FlashMode ;
46
50
import io .flutter .plugins .camera .types .ResolutionPreset ;
@@ -266,7 +270,7 @@ public void takePicture(@NonNull final Result result) {
266
270
},
267
271
null );
268
272
269
- runPicturePreCapture ();
273
+ runPictureAutoFocus ();
270
274
}
271
275
272
276
private final CameraCaptureSession .CaptureCallback pictureCaptureCallback =
@@ -276,18 +280,15 @@ public void onCaptureCompleted(
276
280
@ NonNull CameraCaptureSession session ,
277
281
@ NonNull CaptureRequest request ,
278
282
@ NonNull TotalCaptureResult result ) {
279
- assert (pictureCaptureRequest != null );
280
- switch (pictureCaptureRequest .getState ()) {
281
- case awaitingPreCapture :
282
- Integer aeState = result .get (CaptureResult .CONTROL_AE_STATE );
283
- // Some devices might return null here, in which case we will also continue.
284
- if (aeState == null
285
- || aeState == CaptureRequest .CONTROL_AE_STATE_FLASH_REQUIRED
286
- || aeState == CaptureRequest .CONTROL_AE_STATE_CONVERGED ) {
287
- runPictureCapture ();
288
- }
289
- break ;
290
- }
283
+ processCapture (result );
284
+ }
285
+
286
+ @ Override
287
+ public void onCaptureProgressed (
288
+ @ NonNull CameraCaptureSession session ,
289
+ @ NonNull CaptureRequest request ,
290
+ @ NonNull CaptureResult partialResult ) {
291
+ processCapture (partialResult );
291
292
}
292
293
293
294
@ Override
@@ -309,11 +310,54 @@ public void onCaptureFailed(
309
310
}
310
311
pictureCaptureRequest .error ("captureFailure" , reason , null );
311
312
}
313
+
314
+ private void processCapture (CaptureResult result ) {
315
+ if (pictureCaptureRequest == null ) {
316
+ return ;
317
+ }
318
+
319
+ Integer aeState = result .get (CaptureResult .CONTROL_AE_STATE );
320
+ Integer afState = result .get (CaptureResult .CONTROL_AF_STATE );
321
+ switch (pictureCaptureRequest .getState ()) {
322
+ case focusing :
323
+ if (afState == null ) {
324
+ return ;
325
+ } else if (afState == CaptureResult .CONTROL_AF_STATE_FOCUSED_LOCKED
326
+ || afState == CaptureResult .CONTROL_AF_STATE_NOT_FOCUSED_LOCKED ) {
327
+ // Some devices might return null here, in which case we will also continue.
328
+ if (aeState == null || aeState == CaptureResult .CONTROL_AE_STATE_CONVERGED ) {
329
+ runPictureCapture ();
330
+ } else {
331
+ runPicturePreCapture ();
332
+ }
333
+ }
334
+ break ;
335
+ case preCapture :
336
+ // Some devices might return null here, in which case we will also continue.
337
+ if (aeState == null
338
+ || aeState == CaptureRequest .CONTROL_AE_STATE_PRECAPTURE
339
+ || aeState == CaptureRequest .CONTROL_AE_STATE_FLASH_REQUIRED
340
+ || aeState == CaptureRequest .CONTROL_AE_STATE_CONVERGED ) {
341
+ pictureCaptureRequest .setState (State .waitingPreCaptureReady );
342
+ }
343
+ break ;
344
+ case waitingPreCaptureReady :
345
+ if (aeState == null || aeState != CaptureRequest .CONTROL_AE_STATE_PRECAPTURE ) {
346
+ runPictureCapture ();
347
+ }
348
+ }
349
+ }
312
350
};
313
351
352
+ private void runPictureAutoFocus () {
353
+ assert (pictureCaptureRequest != null );
354
+ pictureCaptureRequest .setState (PictureCaptureRequest .State .focusing );
355
+ lockAutoFocus ();
356
+ }
357
+
314
358
private void runPicturePreCapture () {
315
359
assert (pictureCaptureRequest != null );
316
- pictureCaptureRequest .setState (PictureCaptureRequest .State .awaitingPreCapture );
360
+ pictureCaptureRequest .setState (PictureCaptureRequest .State .preCapture );
317
361
318
362
captureRequestBuilder .set (
319
363
CaptureRequest .CONTROL_AE_PRECAPTURE_TRIGGER ,
@@ -351,7 +395,47 @@ private void runPictureCapture() {
351
395
CaptureRequest .CONTROL_AE_MODE , CaptureRequest .CONTROL_AE_MODE_ON_ALWAYS_FLASH );
352
396
break ;
353
397
}
354
- cameraCaptureSession .capture (captureBuilder .build (), pictureCaptureCallback , null );
398
+ cameraCaptureSession .stopRepeating ();
399
+ cameraCaptureSession .capture (
400
+ captureBuilder .build (),
401
+ new CameraCaptureSession .CaptureCallback () {
402
+ @ Override
403
+ public void onCaptureCompleted (
404
+ @ NonNull CameraCaptureSession session ,
405
+ @ NonNull CaptureRequest request ,
406
+ @ NonNull TotalCaptureResult result ) {
407
+ unlockAutoFocus ();
408
+ }
409
+ },
410
+ null );
411
+ } catch (CameraAccessException e ) {
412
+ pictureCaptureRequest .error ("cameraAccess" , e .getMessage (), null );
413
+ }
414
+ }
415
+
416
+ private void lockAutoFocus () {
417
+ captureRequestBuilder .set (
418
+ CaptureRequest .CONTROL_AF_TRIGGER , CaptureRequest .CONTROL_AF_TRIGGER_START );
419
+ try {
420
+ cameraCaptureSession .capture (captureRequestBuilder .build (), pictureCaptureCallback , null );
421
+ } catch (CameraAccessException e ) {
422
+ pictureCaptureRequest .error ("cameraAccess" , e .getMessage (), null );
423
+ }
424
+ }
425
+
426
+ private void unlockAutoFocus () {
427
+ captureRequestBuilder .set (
428
+ CaptureRequest .CONTROL_AF_TRIGGER , CameraMetadata .CONTROL_AF_TRIGGER_CANCEL );
429
+ initPreviewCaptureBuilder ();
430
+ try {
431
+ cameraCaptureSession .capture (captureRequestBuilder .build (), null , null );
432
+ } catch (CameraAccessException ignored ) {
433
+ }
434
+ captureRequestBuilder .set (
435
+ CaptureRequest .CONTROL_AF_TRIGGER , CaptureRequest .CONTROL_AF_TRIGGER_IDLE );
436
+ try {
437
+ cameraCaptureSession .setRepeatingRequest (
438
+ captureRequestBuilder .build (), pictureCaptureCallback , null );
355
439
} catch (CameraAccessException e ) {
356
440
pictureCaptureRequest .error ("cameraAccess" , e .getMessage (), null );
357
441
}
@@ -397,7 +481,10 @@ public void onConfigured(@NonNull CameraCaptureSession session) {
397
481
}
398
482
cameraCaptureSession = session ;
399
483
initPreviewCaptureBuilder ();
400
- cameraCaptureSession .setRepeatingRequest (captureRequestBuilder .build (), null , null );
484
+ cameraCaptureSession .setRepeatingRequest (
485
+ captureRequestBuilder .build (),
486
+ pictureCaptureCallback ,
487
+ new Handler (Looper .getMainLooper ()));
401
488
if (onSuccessCallback != null ) {
402
489
onSuccessCallback .run ();
403
490
}
@@ -546,12 +633,49 @@ public void setFlashMode(@NonNull final Result result, FlashMode mode)
546
633
result .error ("setFlashModeFailed" , "Device does not have flash capabilities" , null );
547
634
return ;
548
635
}
549
- // Get flash
550
- this .flashMode = mode ;
636
+
637
+ // If switching directly from torch to auto or on, make sure we turn off the torch.
638
+ if (flashMode == FlashMode .torch && mode != FlashMode .torch && mode != FlashMode .off ) {
639
+ this .flashMode = FlashMode .off ;
551
640
initPreviewCaptureBuilder ();
552
- this .cameraCaptureSession .setRepeatingRequest (captureRequestBuilder .build (), null , null );
641
+ this .cameraCaptureSession .setRepeatingRequest (
642
+ captureRequestBuilder .build (),
643
+ new CaptureCallback () {
644
+ private boolean isFinished = false ;
645
+
646
+ @ Override
647
+ public void onCaptureCompleted (
648
+ @ NonNull CameraCaptureSession session ,
649
+ @ NonNull CaptureRequest request ,
650
+ @ NonNull TotalCaptureResult captureResult ) {
651
+ if (isFinished ) {
652
+ return ;
653
+ }
654
+
655
+ updateFlash (mode );
656
+ result .success (null );
657
+ isFinished = true ;
658
+ }
659
+
660
+ @ Override
661
+ public void onCaptureFailed (
662
+ @ NonNull CameraCaptureSession session ,
663
+ @ NonNull CaptureRequest request ,
664
+ @ NonNull CaptureFailure failure ) {
665
+ if (isFinished ) {
666
+ return ;
667
+ }
668
+
669
+ result .error ("setFlashModeFailed" , "Could not set flash mode." , null );
670
+ isFinished = true ;
671
+ }
672
+ },
673
+ null );
674
+ } else {
675
+ updateFlash (mode );
553
676
result .success (null );
554
677
}
678
+ }
555
679
556
680
public void setWbMode (@ NonNull final Result result , WbMode mode )
557
681
throws CameraAccessException {
@@ -741,6 +865,18 @@ public void setIsoValue(@NonNull final Result result, int value)
741
865
result .success (value );
742
866
}
743
867
868
+ private void updateFlash (FlashMode mode ) {
869
+ // Get flash
870
+ flashMode = mode ;
871
+ initPreviewCaptureBuilder ();
872
+ try {
873
+ cameraCaptureSession .setRepeatingRequest (
874
+ captureRequestBuilder .build (), pictureCaptureCallback , null );
875
+ } catch (CameraAccessException e ) {
876
+ pictureCaptureRequest .error ("cameraAccess" , e .getMessage (), null );
877
+ }
878
+ }
879
+
744
880
private void initPreviewCaptureBuilder () {
745
881
captureRequestBuilder .set (CaptureRequest .CONTROL_MODE , CaptureRequest .CONTROL_MODE_AUTO );
746
882
// Applying flash modes
@@ -767,6 +903,7 @@ private void initPreviewCaptureBuilder() {
767
903
captureRequestBuilder .set (CaptureRequest .FLASH_MODE , CaptureRequest .FLASH_MODE_TORCH );
768
904
break ;
769
905
}
906
+
770
907
// Applying auto exposure
771
908
if (aeMeteringRectangle != null ) {
772
909
captureRequestBuilder .set (
0 commit comments