2121import android .hardware .camera2 .CaptureRequest ;
2222import android .hardware .camera2 .CaptureResult ;
2323import android .hardware .camera2 .TotalCaptureResult ;
24+ import android .hardware .camera2 .params .MeteringRectangle ;
2425import android .hardware .camera2 .params .OutputConfiguration ;
2526import android .hardware .camera2 .params .SessionConfiguration ;
2627import android .media .CamcorderProfile ;
3233import android .os .Build .VERSION_CODES ;
3334import android .os .Handler ;
3435import android .os .Looper ;
36+ import android .util .Range ;
37+ import android .util .Rational ;
3538import android .util .Size ;
3639import android .view .OrientationEventListener ;
3740import android .view .Surface ;
4043import io .flutter .plugin .common .MethodChannel .Result ;
4144import io .flutter .plugins .camera .PictureCaptureRequest .State ;
4245import io .flutter .plugins .camera .media .MediaRecorderBuilder ;
46+ import io .flutter .plugins .camera .types .ExposureMode ;
4347import io .flutter .plugins .camera .types .FlashMode ;
4448import io .flutter .plugins .camera .types .ResolutionPreset ;
4549import io .flutter .view .TextureRegistry .SurfaceTextureEntry ;
@@ -80,7 +84,10 @@ public class Camera {
8084 private File videoRecordingFile ;
8185 private int currentOrientation = ORIENTATION_UNKNOWN ;
8286 private FlashMode flashMode ;
87+ private ExposureMode exposureMode ;
8388 private PictureCaptureRequest pictureCaptureRequest ;
89+ private CameraRegions cameraRegions ;
90+ private int exposureOffset ;
8491
8592 public Camera (
8693 final Activity activity ,
@@ -100,6 +107,8 @@ public Camera(
100107 this .cameraManager = (CameraManager ) activity .getSystemService (Context .CAMERA_SERVICE );
101108 this .applicationContext = activity .getApplicationContext ();
102109 this .flashMode = FlashMode .auto ;
110+ this .exposureMode = ExposureMode .auto ;
111+ this .exposureOffset = 0 ;
103112 orientationEventListener =
104113 new OrientationEventListener (activity .getApplicationContext ()) {
105114 @ Override
@@ -158,15 +167,17 @@ public void open() throws CameraAccessException {
158167 public void onOpened (@ NonNull CameraDevice device ) {
159168 cameraDevice = device ;
160169 try {
170+ cameraRegions = new CameraRegions (getRegionBoundaries ());
161171 startPreview ();
172+ dartMessenger .sendCameraInitializedEvent (
173+ previewSize .getWidth (),
174+ previewSize .getHeight (),
175+ exposureMode ,
176+ isExposurePointSupported ());
162177 } catch (CameraAccessException e ) {
163178 dartMessenger .sendCameraErrorEvent (e .getMessage ());
164179 close ();
165- return ;
166180 }
167-
168- dartMessenger .sendCameraInitializedEvent (
169- previewSize .getWidth (), previewSize .getHeight ());
170181 }
171182
172183 @ Override
@@ -605,16 +616,11 @@ public void resumeVideoRecording(@NonNull final Result result) {
605616 public void setFlashMode (@ NonNull final Result result , FlashMode mode )
606617 throws CameraAccessException {
607618 // 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+
618624 // Check if flash is available.
619625 if (flashAvailable == null || !flashAvailable ) {
620626 result .error ("setFlashModeFailed" , "Device does not have flash capabilities" , null );
@@ -676,8 +682,133 @@ private void updateFlash(FlashMode mode) {
676682 }
677683 }
678684
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+
679809 private void initPreviewCaptureBuilder () {
680810 captureRequestBuilder .set (CaptureRequest .CONTROL_MODE , CaptureRequest .CONTROL_MODE_AUTO );
811+ // Applying flash modes
681812 switch (flashMode ) {
682813 case off :
683814 captureRequestBuilder .set (
@@ -701,6 +832,22 @@ private void initPreviewCaptureBuilder() {
701832 captureRequestBuilder .set (CaptureRequest .FLASH_MODE , CaptureRequest .FLASH_MODE_TORCH );
702833 break ;
703834 }
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
704851 captureRequestBuilder .set (
705852 CaptureRequest .CONTROL_AF_MODE , CaptureRequest .CONTROL_AF_MODE_CONTINUOUS_PICTURE );
706853 }
0 commit comments