21
21
import android .hardware .camera2 .CaptureRequest ;
22
22
import android .hardware .camera2 .CaptureResult ;
23
23
import android .hardware .camera2 .TotalCaptureResult ;
24
+ import android .hardware .camera2 .params .MeteringRectangle ;
24
25
import android .hardware .camera2 .params .OutputConfiguration ;
25
26
import android .hardware .camera2 .params .SessionConfiguration ;
26
27
import android .media .CamcorderProfile ;
32
33
import android .os .Build .VERSION_CODES ;
33
34
import android .os .Handler ;
34
35
import android .os .Looper ;
36
+ import android .util .Range ;
37
+ import android .util .Rational ;
35
38
import android .util .Size ;
36
39
import android .view .OrientationEventListener ;
37
40
import android .view .Surface ;
40
43
import io .flutter .plugin .common .MethodChannel .Result ;
41
44
import io .flutter .plugins .camera .PictureCaptureRequest .State ;
42
45
import io .flutter .plugins .camera .media .MediaRecorderBuilder ;
46
+ import io .flutter .plugins .camera .types .ExposureMode ;
43
47
import io .flutter .plugins .camera .types .FlashMode ;
44
48
import io .flutter .plugins .camera .types .ResolutionPreset ;
45
49
import io .flutter .view .TextureRegistry .SurfaceTextureEntry ;
@@ -80,7 +84,10 @@ public class Camera {
80
84
private File videoRecordingFile ;
81
85
private int currentOrientation = ORIENTATION_UNKNOWN ;
82
86
private FlashMode flashMode ;
87
+ private ExposureMode exposureMode ;
83
88
private PictureCaptureRequest pictureCaptureRequest ;
89
+ private CameraRegions cameraRegions ;
90
+ private int exposureOffset ;
84
91
85
92
public Camera (
86
93
final Activity activity ,
@@ -100,6 +107,8 @@ public Camera(
100
107
this .cameraManager = (CameraManager ) activity .getSystemService (Context .CAMERA_SERVICE );
101
108
this .applicationContext = activity .getApplicationContext ();
102
109
this .flashMode = FlashMode .auto ;
110
+ this .exposureMode = ExposureMode .auto ;
111
+ this .exposureOffset = 0 ;
103
112
orientationEventListener =
104
113
new OrientationEventListener (activity .getApplicationContext ()) {
105
114
@ Override
@@ -158,15 +167,17 @@ public void open() throws CameraAccessException {
158
167
public void onOpened (@ NonNull CameraDevice device ) {
159
168
cameraDevice = device ;
160
169
try {
170
+ cameraRegions = new CameraRegions (getRegionBoundaries ());
161
171
startPreview ();
172
+ dartMessenger .sendCameraInitializedEvent (
173
+ previewSize .getWidth (),
174
+ previewSize .getHeight (),
175
+ exposureMode ,
176
+ isExposurePointSupported ());
162
177
} catch (CameraAccessException e ) {
163
178
dartMessenger .sendCameraErrorEvent (e .getMessage ());
164
179
close ();
165
- return ;
166
180
}
167
-
168
- dartMessenger .sendCameraInitializedEvent (
169
- previewSize .getWidth (), previewSize .getHeight ());
170
181
}
171
182
172
183
@ Override
@@ -605,16 +616,11 @@ public void resumeVideoRecording(@NonNull final Result result) {
605
616
public void setFlashMode (@ NonNull final Result result , FlashMode mode )
606
617
throws CameraAccessException {
607
618
// Get the flash availability
608
- Boolean flashAvailable ;
609
- try {
610
- flashAvailable =
611
- cameraManager
612
- .getCameraCharacteristics (cameraDevice .getId ())
613
- .get (CameraCharacteristics .FLASH_INFO_AVAILABLE );
614
- } catch (CameraAccessException e ) {
615
- result .error ("setFlashModeFailed" , e .getMessage (), null );
616
- return ;
617
- }
619
+ Boolean flashAvailable =
620
+ cameraManager
621
+ .getCameraCharacteristics (cameraDevice .getId ())
622
+ .get (CameraCharacteristics .FLASH_INFO_AVAILABLE );
623
+
618
624
// Check if flash is available.
619
625
if (flashAvailable == null || !flashAvailable ) {
620
626
result .error ("setFlashModeFailed" , "Device does not have flash capabilities" , null );
@@ -676,8 +682,133 @@ private void updateFlash(FlashMode mode) {
676
682
}
677
683
}
678
684
685
+ public void setExposureMode (@ NonNull final Result result , ExposureMode mode )
686
+ throws CameraAccessException {
687
+ this .exposureMode = mode ;
688
+ initPreviewCaptureBuilder ();
689
+ cameraCaptureSession .setRepeatingRequest (captureRequestBuilder .build (), null , null );
690
+ result .success (null );
691
+ }
692
+
693
+ public void setExposurePoint (@ NonNull final Result result , Double x , Double y )
694
+ throws CameraAccessException {
695
+ // Check if exposure point functionality is available.
696
+ if (!isExposurePointSupported ()) {
697
+ result .error (
698
+ "setExposurePointFailed" , "Device does not have exposure point capabilities" , null );
699
+ return ;
700
+ }
701
+ // Check if we are doing a reset or not
702
+ if (x == null || y == null ) {
703
+ x = 0.5 ;
704
+ y = 0.5 ;
705
+ }
706
+ // Get the current region boundaries.
707
+ Size maxBoundaries = getRegionBoundaries ();
708
+ if (maxBoundaries == null ) {
709
+ result .error ("setExposurePointFailed" , "Could not determine max region boundaries" , null );
710
+ return ;
711
+ }
712
+ // Set the metering rectangle
713
+ cameraRegions .setAutoExposureMeteringRectangleFromPoint (x , y );
714
+ // Apply it
715
+ initPreviewCaptureBuilder ();
716
+ this .cameraCaptureSession .setRepeatingRequest (
717
+ captureRequestBuilder .build (), pictureCaptureCallback , null );
718
+ result .success (null );
719
+ }
720
+
721
+ @ TargetApi (VERSION_CODES .P )
722
+ private boolean supportsDistortionCorrection () throws CameraAccessException {
723
+ int [] availableDistortionCorrectionModes =
724
+ cameraManager
725
+ .getCameraCharacteristics (cameraDevice .getId ())
726
+ .get (CameraCharacteristics .DISTORTION_CORRECTION_AVAILABLE_MODES );
727
+ if (availableDistortionCorrectionModes == null ) availableDistortionCorrectionModes = new int [0 ];
728
+ long nonOffModesSupported =
729
+ Arrays .stream (availableDistortionCorrectionModes )
730
+ .filter ((value ) -> value != CaptureRequest .DISTORTION_CORRECTION_MODE_OFF )
731
+ .count ();
732
+ return nonOffModesSupported > 0 ;
733
+ }
734
+
735
+ private Size getRegionBoundaries () throws CameraAccessException {
736
+ // No distortion correction support
737
+ if (android .os .Build .VERSION .SDK_INT < VERSION_CODES .P || !supportsDistortionCorrection ()) {
738
+ return cameraManager
739
+ .getCameraCharacteristics (cameraDevice .getId ())
740
+ .get (CameraCharacteristics .SENSOR_INFO_PIXEL_ARRAY_SIZE );
741
+ }
742
+ // Get the current distortion correction mode
743
+ Integer distortionCorrectionMode =
744
+ captureRequestBuilder .get (CaptureRequest .DISTORTION_CORRECTION_MODE );
745
+ // Return the correct boundaries depending on the mode
746
+ android .graphics .Rect rect ;
747
+ if (distortionCorrectionMode == null
748
+ || distortionCorrectionMode == CaptureRequest .DISTORTION_CORRECTION_MODE_OFF ) {
749
+ rect =
750
+ cameraManager
751
+ .getCameraCharacteristics (cameraDevice .getId ())
752
+ .get (CameraCharacteristics .SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE );
753
+ } else {
754
+ rect =
755
+ cameraManager
756
+ .getCameraCharacteristics (cameraDevice .getId ())
757
+ .get (CameraCharacteristics .SENSOR_INFO_ACTIVE_ARRAY_SIZE );
758
+ }
759
+ return rect == null ? null : new Size (rect .width (), rect .height ());
760
+ }
761
+
762
+ private boolean isExposurePointSupported () throws CameraAccessException {
763
+ Integer supportedRegions =
764
+ cameraManager
765
+ .getCameraCharacteristics (cameraDevice .getId ())
766
+ .get (CameraCharacteristics .CONTROL_MAX_REGIONS_AE );
767
+ return supportedRegions != null && supportedRegions > 0 ;
768
+ }
769
+
770
+ public double getMinExposureOffset () throws CameraAccessException {
771
+ Range <Integer > range =
772
+ cameraManager
773
+ .getCameraCharacteristics (cameraDevice .getId ())
774
+ .get (CameraCharacteristics .CONTROL_AE_COMPENSATION_RANGE );
775
+ double minStepped = range == null ? 0 : range .getLower ();
776
+ double stepSize = getExposureOffsetStepSize ();
777
+ return minStepped * stepSize ;
778
+ }
779
+
780
+ public double getMaxExposureOffset () throws CameraAccessException {
781
+ Range <Integer > range =
782
+ cameraManager
783
+ .getCameraCharacteristics (cameraDevice .getId ())
784
+ .get (CameraCharacteristics .CONTROL_AE_COMPENSATION_RANGE );
785
+ double maxStepped = range == null ? 0 : range .getUpper ();
786
+ double stepSize = getExposureOffsetStepSize ();
787
+ return maxStepped * stepSize ;
788
+ }
789
+
790
+ public double getExposureOffsetStepSize () throws CameraAccessException {
791
+ Rational stepSize =
792
+ cameraManager
793
+ .getCameraCharacteristics (cameraDevice .getId ())
794
+ .get (CameraCharacteristics .CONTROL_AE_COMPENSATION_STEP );
795
+ return stepSize == null ? 0.0 : stepSize .doubleValue ();
796
+ }
797
+
798
+ public void setExposureOffset (@ NonNull final Result result , double offset )
799
+ throws CameraAccessException {
800
+ // Set the exposure offset
801
+ double stepSize = getExposureOffsetStepSize ();
802
+ exposureOffset = (int ) (offset / stepSize );
803
+ // Apply it
804
+ initPreviewCaptureBuilder ();
805
+ this .cameraCaptureSession .setRepeatingRequest (captureRequestBuilder .build (), null , null );
806
+ result .success (offset );
807
+ }
808
+
679
809
private void initPreviewCaptureBuilder () {
680
810
captureRequestBuilder .set (CaptureRequest .CONTROL_MODE , CaptureRequest .CONTROL_MODE_AUTO );
811
+ // Applying flash modes
681
812
switch (flashMode ) {
682
813
case off :
683
814
captureRequestBuilder .set (
@@ -701,6 +832,22 @@ private void initPreviewCaptureBuilder() {
701
832
captureRequestBuilder .set (CaptureRequest .FLASH_MODE , CaptureRequest .FLASH_MODE_TORCH );
702
833
break ;
703
834
}
835
+ // Applying auto exposure
836
+ MeteringRectangle aeRect = cameraRegions .getAEMeteringRectangle ();
837
+ captureRequestBuilder .set (
838
+ CaptureRequest .CONTROL_AE_REGIONS ,
839
+ aeRect == null ? null : new MeteringRectangle [] {cameraRegions .getAEMeteringRectangle ()});
840
+ switch (exposureMode ) {
841
+ case locked :
842
+ captureRequestBuilder .set (CaptureRequest .CONTROL_AE_LOCK , true );
843
+ break ;
844
+ case auto :
845
+ default :
846
+ captureRequestBuilder .set (CaptureRequest .CONTROL_AE_LOCK , false );
847
+ break ;
848
+ }
849
+ captureRequestBuilder .set (CaptureRequest .CONTROL_AE_EXPOSURE_COMPENSATION , exposureOffset );
850
+ // Applying auto focus
704
851
captureRequestBuilder .set (
705
852
CaptureRequest .CONTROL_AF_MODE , CaptureRequest .CONTROL_AF_MODE_CONTINUOUS_PICTURE );
706
853
}
0 commit comments