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 ;
29
30
import android .os .Build ;
30
31
import android .os .Build .VERSION ;
31
32
import android .os .Build .VERSION_CODES ;
33
+ import android .os .Handler ;
34
+ import android .os .Looper ;
32
35
import android .util .Size ;
33
36
import android .view .OrientationEventListener ;
34
37
import android .view .Surface ;
35
38
import androidx .annotation .NonNull ;
36
39
import io .flutter .plugin .common .EventChannel ;
37
40
import io .flutter .plugin .common .MethodChannel .Result ;
41
+ import io .flutter .plugins .camera .PictureCaptureRequest .State ;
38
42
import io .flutter .plugins .camera .media .MediaRecorderBuilder ;
39
43
import io .flutter .plugins .camera .types .FlashMode ;
40
44
import io .flutter .plugins .camera .types .ResolutionPreset ;
@@ -246,7 +250,7 @@ public void takePicture(@NonNull final Result result) {
246
250
},
247
251
null );
248
252
249
- runPicturePreCapture ();
253
+ runPictureAutoFocus ();
250
254
}
251
255
252
256
private final CameraCaptureSession .CaptureCallback pictureCaptureCallback =
@@ -256,18 +260,15 @@ public void onCaptureCompleted(
256
260
@ NonNull CameraCaptureSession session ,
257
261
@ NonNull CaptureRequest request ,
258
262
@ NonNull TotalCaptureResult result ) {
259
- assert (pictureCaptureRequest != null );
260
- switch (pictureCaptureRequest .getState ()) {
261
- case awaitingPreCapture :
262
- Integer aeState = result .get (CaptureResult .CONTROL_AE_STATE );
263
- // Some devices might return null here, in which case we will also continue.
264
- if (aeState == null
265
- || aeState == CaptureRequest .CONTROL_AE_STATE_FLASH_REQUIRED
266
- || aeState == CaptureRequest .CONTROL_AE_STATE_CONVERGED ) {
267
- runPictureCapture ();
268
- }
269
- break ;
270
- }
263
+ processCapture (result );
264
+ }
265
+
266
+ @ Override
267
+ public void onCaptureProgressed (
268
+ @ NonNull CameraCaptureSession session ,
269
+ @ NonNull CaptureRequest request ,
270
+ @ NonNull CaptureResult partialResult ) {
271
+ processCapture (partialResult );
271
272
}
272
273
273
274
@ Override
@@ -289,11 +290,54 @@ public void onCaptureFailed(
289
290
}
290
291
pictureCaptureRequest .error ("captureFailure" , reason , null );
291
292
}
293
+
294
+ private void processCapture (CaptureResult result ) {
295
+ if (pictureCaptureRequest == null ) {
296
+ return ;
297
+ }
298
+
299
+ Integer aeState = result .get (CaptureResult .CONTROL_AE_STATE );
300
+ Integer afState = result .get (CaptureResult .CONTROL_AF_STATE );
301
+ switch (pictureCaptureRequest .getState ()) {
302
+ case focusing :
303
+ if (afState == null ) {
304
+ return ;
305
+ } else if (afState == CaptureResult .CONTROL_AF_STATE_FOCUSED_LOCKED
306
+ || afState == CaptureResult .CONTROL_AF_STATE_NOT_FOCUSED_LOCKED ) {
307
+ // Some devices might return null here, in which case we will also continue.
308
+ if (aeState == null || aeState == CaptureResult .CONTROL_AE_STATE_CONVERGED ) {
309
+ runPictureCapture ();
310
+ } else {
311
+ runPicturePreCapture ();
312
+ }
313
+ }
314
+ break ;
315
+ case preCapture :
316
+ // Some devices might return null here, in which case we will also continue.
317
+ if (aeState == null
318
+ || aeState == CaptureRequest .CONTROL_AE_STATE_PRECAPTURE
319
+ || aeState == CaptureRequest .CONTROL_AE_STATE_FLASH_REQUIRED
320
+ || aeState == CaptureRequest .CONTROL_AE_STATE_CONVERGED ) {
321
+ pictureCaptureRequest .setState (State .waitingPreCaptureReady );
322
+ }
323
+ break ;
324
+ case waitingPreCaptureReady :
325
+ if (aeState == null || aeState != CaptureRequest .CONTROL_AE_STATE_PRECAPTURE ) {
326
+ runPictureCapture ();
327
+ }
328
+ }
329
+ }
292
330
};
293
331
332
+ private void runPictureAutoFocus () {
333
+ assert (pictureCaptureRequest != null );
334
+ pictureCaptureRequest .setState (PictureCaptureRequest .State .focusing );
335
+ lockAutoFocus ();
336
+ }
337
+
294
338
private void runPicturePreCapture () {
295
339
assert (pictureCaptureRequest != null );
296
- pictureCaptureRequest .setState (PictureCaptureRequest .State .awaitingPreCapture );
340
+ pictureCaptureRequest .setState (PictureCaptureRequest .State .preCapture );
297
341
298
342
captureRequestBuilder .set (
299
343
CaptureRequest .CONTROL_AE_PRECAPTURE_TRIGGER ,
@@ -331,7 +375,47 @@ private void runPictureCapture() {
331
375
CaptureRequest .CONTROL_AE_MODE , CaptureRequest .CONTROL_AE_MODE_ON_ALWAYS_FLASH );
332
376
break ;
333
377
}
334
- cameraCaptureSession .capture (captureBuilder .build (), pictureCaptureCallback , null );
378
+ cameraCaptureSession .stopRepeating ();
379
+ cameraCaptureSession .capture (
380
+ captureBuilder .build (),
381
+ new CameraCaptureSession .CaptureCallback () {
382
+ @ Override
383
+ public void onCaptureCompleted (
384
+ @ NonNull CameraCaptureSession session ,
385
+ @ NonNull CaptureRequest request ,
386
+ @ NonNull TotalCaptureResult result ) {
387
+ unlockAutoFocus ();
388
+ }
389
+ },
390
+ null );
391
+ } catch (CameraAccessException e ) {
392
+ pictureCaptureRequest .error ("cameraAccess" , e .getMessage (), null );
393
+ }
394
+ }
395
+
396
+ private void lockAutoFocus () {
397
+ captureRequestBuilder .set (
398
+ CaptureRequest .CONTROL_AF_TRIGGER , CaptureRequest .CONTROL_AF_TRIGGER_START );
399
+ try {
400
+ cameraCaptureSession .capture (captureRequestBuilder .build (), pictureCaptureCallback , null );
401
+ } catch (CameraAccessException e ) {
402
+ pictureCaptureRequest .error ("cameraAccess" , e .getMessage (), null );
403
+ }
404
+ }
405
+
406
+ private void unlockAutoFocus () {
407
+ captureRequestBuilder .set (
408
+ CaptureRequest .CONTROL_AF_TRIGGER , CameraMetadata .CONTROL_AF_TRIGGER_CANCEL );
409
+ initPreviewCaptureBuilder ();
410
+ try {
411
+ cameraCaptureSession .capture (captureRequestBuilder .build (), null , null );
412
+ } catch (CameraAccessException ignored ) {
413
+ }
414
+ captureRequestBuilder .set (
415
+ CaptureRequest .CONTROL_AF_TRIGGER , CaptureRequest .CONTROL_AF_TRIGGER_IDLE );
416
+ try {
417
+ cameraCaptureSession .setRepeatingRequest (
418
+ captureRequestBuilder .build (), pictureCaptureCallback , null );
335
419
} catch (CameraAccessException e ) {
336
420
pictureCaptureRequest .error ("cameraAccess" , e .getMessage (), null );
337
421
}
@@ -377,7 +461,10 @@ public void onConfigured(@NonNull CameraCaptureSession session) {
377
461
}
378
462
cameraCaptureSession = session ;
379
463
initPreviewCaptureBuilder ();
380
- cameraCaptureSession .setRepeatingRequest (captureRequestBuilder .build (), null , null );
464
+ cameraCaptureSession .setRepeatingRequest (
465
+ captureRequestBuilder .build (),
466
+ pictureCaptureCallback ,
467
+ new Handler (Looper .getMainLooper ()));
381
468
if (onSuccessCallback != null ) {
382
469
onSuccessCallback .run ();
383
470
}
@@ -531,11 +618,60 @@ public void setFlashMode(@NonNull final Result result, FlashMode mode)
531
618
result .error ("setFlashModeFailed" , "Device does not have flash capabilities" , null );
532
619
return ;
533
620
}
621
+
622
+ // If switching directly from torch to auto or on, make sure we turn off the torch.
623
+ if (flashMode == FlashMode .torch && mode != FlashMode .torch && mode != FlashMode .off ) {
624
+ this .flashMode = FlashMode .off ;
625
+ initPreviewCaptureBuilder ();
626
+ this .cameraCaptureSession .setRepeatingRequest (
627
+ captureRequestBuilder .build (),
628
+ new CaptureCallback () {
629
+ private boolean isFinished = false ;
630
+
631
+ @ Override
632
+ public void onCaptureCompleted (
633
+ @ NonNull CameraCaptureSession session ,
634
+ @ NonNull CaptureRequest request ,
635
+ @ NonNull TotalCaptureResult captureResult ) {
636
+ if (isFinished ) {
637
+ return ;
638
+ }
639
+
640
+ updateFlash (mode );
641
+ result .success (null );
642
+ isFinished = true ;
643
+ }
644
+
645
+ @ Override
646
+ public void onCaptureFailed (
647
+ @ NonNull CameraCaptureSession session ,
648
+ @ NonNull CaptureRequest request ,
649
+ @ NonNull CaptureFailure failure ) {
650
+ if (isFinished ) {
651
+ return ;
652
+ }
653
+
654
+ result .error ("setFlashModeFailed" , "Could not set flash mode." , null );
655
+ isFinished = true ;
656
+ }
657
+ },
658
+ null );
659
+ } else {
660
+ updateFlash (mode );
661
+ result .success (null );
662
+ }
663
+ }
664
+
665
+ private void updateFlash (FlashMode mode ) {
534
666
// Get flash
535
- this . flashMode = mode ;
667
+ flashMode = mode ;
536
668
initPreviewCaptureBuilder ();
537
- this .cameraCaptureSession .setRepeatingRequest (captureRequestBuilder .build (), null , null );
538
- result .success (null );
669
+ try {
670
+ cameraCaptureSession .setRepeatingRequest (
671
+ captureRequestBuilder .build (), pictureCaptureCallback , null );
672
+ } catch (CameraAccessException e ) {
673
+ pictureCaptureRequest .error ("cameraAccess" , e .getMessage (), null );
674
+ }
539
675
}
540
676
541
677
private void initPreviewCaptureBuilder () {
@@ -563,6 +699,8 @@ private void initPreviewCaptureBuilder() {
563
699
captureRequestBuilder .set (CaptureRequest .FLASH_MODE , CaptureRequest .FLASH_MODE_TORCH );
564
700
break ;
565
701
}
702
+ captureRequestBuilder .set (
703
+ CaptureRequest .CONTROL_AF_MODE , CaptureRequest .CONTROL_AF_MODE_CONTINUOUS_PICTURE );
566
704
}
567
705
568
706
public void startPreview () throws CameraAccessException {
0 commit comments