[camera_android] Provides a default exposure point if null. (#3851)

Recently we had problems in our apps (they use the camera a lot), due to the error of [CONTROL_AE_REGIONS] being set to a null value.

It should now return the default exposure point value. Thanks https://github.com/AnggaSP

Fix attempt for https://github.com/flutter/flutter/issues/105200
diff --git a/packages/camera/camera_android/CHANGELOG.md b/packages/camera/camera_android/CHANGELOG.md
index a0a0fd8..426731a 100644
--- a/packages/camera/camera_android/CHANGELOG.md
+++ b/packages/camera/camera_android/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.10.8+5
+
+* Provides a default exposure point if null.
+
 ## 0.10.8+4
 
 * Adjusts SDK checks for better testability.
diff --git a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/exposurepoint/ExposurePointFeature.java b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/exposurepoint/ExposurePointFeature.java
index 40f28ea..afeed98 100644
--- a/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/exposurepoint/ExposurePointFeature.java
+++ b/packages/camera/camera_android/android/src/main/java/io/flutter/plugins/camera/features/exposurepoint/ExposurePointFeature.java
@@ -10,6 +10,7 @@
 import android.util.Size;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
 import io.flutter.embedding.engine.systemchannels.PlatformChannel;
 import io.flutter.plugins.camera.CameraProperties;
 import io.flutter.plugins.camera.CameraRegionUtils;
@@ -24,6 +25,8 @@
   @Nullable private Point exposurePoint;
   private MeteringRectangle exposureRectangle;
   @NonNull private final SensorOrientationFeature sensorOrientationFeature;
+  private boolean defaultRegionsHasBeenSet = false;
+  @VisibleForTesting @Nullable public MeteringRectangle[] defaultRegions;
 
   /**
    * Creates a new instance of the {@link ExposurePointFeature}.
@@ -78,9 +81,18 @@
     if (!checkIsSupported()) {
       return;
     }
-    requestBuilder.set(
-        CaptureRequest.CONTROL_AE_REGIONS,
-        exposureRectangle == null ? null : new MeteringRectangle[] {exposureRectangle});
+
+    if (!defaultRegionsHasBeenSet) {
+      defaultRegions = requestBuilder.get(CaptureRequest.CONTROL_AE_REGIONS);
+      defaultRegionsHasBeenSet = true;
+    }
+
+    if (exposureRectangle != null) {
+      requestBuilder.set(
+          CaptureRequest.CONTROL_AE_REGIONS, new MeteringRectangle[] {exposureRectangle});
+    } else {
+      requestBuilder.set(CaptureRequest.CONTROL_AE_REGIONS, defaultRegions);
+    }
   }
 
   private void buildExposureRectangle() {
diff --git a/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/exposurepoint/ExposurePointFeatureTest.java b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/exposurepoint/ExposurePointFeatureTest.java
index 800c3f7..1eaa537 100644
--- a/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/exposurepoint/ExposurePointFeatureTest.java
+++ b/packages/camera/camera_android/android/src/test/java/io/flutter/plugins/camera/features/exposurepoint/ExposurePointFeatureTest.java
@@ -9,7 +9,7 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
@@ -281,7 +281,7 @@
   }
 
   @Test
-  public void updateBuilder_shouldNotSetMeteringRectangleWhenNoValidBoundariesAreSupplied() {
+  public void updateBuilder_shoulSetDefaultMeteringRectangleWhenNoValidBoundariesAreSupplied() {
     CameraProperties mockCameraProperties = mock(CameraProperties.class);
     when(mockCameraProperties.getControlMaxRegionsAutoExposure()).thenReturn(1);
     CaptureRequest.Builder mockCaptureRequestBuilder = mock(CaptureRequest.Builder.class);
@@ -290,11 +290,11 @@
 
     exposurePointFeature.updateBuilder(mockCaptureRequestBuilder);
 
-    verify(mockCaptureRequestBuilder, times(1)).set(any(), isNull());
+    verify(mockCaptureRequestBuilder, times(1)).set(any(), any());
   }
 
   @Test
-  public void updateBuilder_shouldNotSetMeteringRectangleWhenNoValidCoordsAreSupplied() {
+  public void updateBuilder_shouldSetDefaultMeteringRectangleWhenNoValidCoordsAreSupplied() {
     CameraProperties mockCameraProperties = mock(CameraProperties.class);
     when(mockCameraProperties.getControlMaxRegionsAutoExposure()).thenReturn(1);
     CaptureRequest.Builder mockCaptureRequestBuilder = mock(CaptureRequest.Builder.class);
@@ -302,12 +302,40 @@
         new ExposurePointFeature(mockCameraProperties, mockSensorOrientationFeature);
     exposurePointFeature.setCameraBoundaries(this.mockCameraBoundaries);
 
+    MeteringRectangle[] defaultRegions =
+        new MeteringRectangle[] {new MeteringRectangle(0, 0, 100, 100, 0)};
+    when(mockCaptureRequestBuilder.get(CaptureRequest.CONTROL_AE_REGIONS))
+        .thenReturn(defaultRegions);
+
     exposurePointFeature.setValue(null);
     exposurePointFeature.updateBuilder(mockCaptureRequestBuilder);
     exposurePointFeature.setValue(new Point(0d, null));
     exposurePointFeature.updateBuilder(mockCaptureRequestBuilder);
     exposurePointFeature.setValue(new Point(null, 0d));
     exposurePointFeature.updateBuilder(mockCaptureRequestBuilder);
-    verify(mockCaptureRequestBuilder, times(3)).set(any(), isNull());
+
+    verify(mockCaptureRequestBuilder, times(3)).set(any(), eq(defaultRegions));
+  }
+
+  @Test
+  public void updateBuilder_shouldSetNonNullMeteringRectangleWhenNullValueIsSupplied() {
+    CameraProperties mockCameraProperties = mock(CameraProperties.class);
+    when(mockCameraProperties.getControlMaxRegionsAutoExposure()).thenReturn(1);
+    CaptureRequest.Builder mockCaptureRequestBuilder = mock(CaptureRequest.Builder.class);
+    ExposurePointFeature exposurePointFeature =
+        new ExposurePointFeature(mockCameraProperties, mockSensorOrientationFeature);
+
+    MeteringRectangle[] defaultRegions =
+        new MeteringRectangle[] {new MeteringRectangle(0, 0, 100, 100, 0)};
+    when(mockCaptureRequestBuilder.get(CaptureRequest.CONTROL_AE_REGIONS))
+        .thenReturn(defaultRegions);
+
+    exposurePointFeature.setCameraBoundaries(this.mockCameraBoundaries);
+
+    exposurePointFeature.setValue(null);
+
+    exposurePointFeature.updateBuilder(mockCaptureRequestBuilder);
+
+    verify(mockCaptureRequestBuilder, times(1)).set(any(), eq(exposurePointFeature.defaultRegions));
   }
 }
diff --git a/packages/camera/camera_android/pubspec.yaml b/packages/camera/camera_android/pubspec.yaml
index 307506a..68e539e 100644
--- a/packages/camera/camera_android/pubspec.yaml
+++ b/packages/camera/camera_android/pubspec.yaml
@@ -3,7 +3,7 @@
 repository: https://github.com/flutter/packages/tree/main/packages/camera/camera_android
 issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+camera%22
 
-version: 0.10.8+4
+version: 0.10.8+5
 
 environment:
   sdk: ">=2.18.0 <4.0.0"