From baae5f6bc22b72f99d1535e448186d354d44f535 Mon Sep 17 00:00:00 2001
From: BeMacized <info@bemacized.net>
Date: Thu, 10 Jun 2021 14:59:00 +0200
Subject: [PATCH 1/4] Add feature classes for exposure- and focus point
 functionality.

---
 packages/camera/camera/android/build.gradle   |   2 +-
 .../plugins/camera/CameraRegionUtils.java     | 138 ++++++++
 .../exposurepoint/ExposurePointFeature.java   |  61 ++--
 .../focuspoint/FocusPointFeature.java         |  87 ++++++
 .../plugins/camera/types/CameraRegions.java   | 199 ------------
 .../plugins/camera/CameraRegionUtilsTest.java | 294 ++++++++++++++++++
 .../ExposurePointFeatureTest.java             | 220 ++++++++-----
 .../focuspoint/FocusPointFeatureTest.java     | 270 ++++++++++++++++
 .../types/CameraRegionsFactoryTest.java       | 201 ------------
 .../camera/types/CameraRegionsTest.java       | 114 -------
 10 files changed, 972 insertions(+), 614 deletions(-)
 create mode 100644 packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/CameraRegionUtils.java
 create mode 100644 packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/focuspoint/FocusPointFeature.java
 delete mode 100644 packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/types/CameraRegions.java
 create mode 100644 packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/CameraRegionUtilsTest.java
 create mode 100644 packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/focuspoint/FocusPointFeatureTest.java
 delete mode 100644 packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/types/CameraRegionsFactoryTest.java
 delete mode 100644 packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/types/CameraRegionsTest.java

diff --git a/packages/camera/camera/android/build.gradle b/packages/camera/camera/android/build.gradle
index 0907c1eeecc9..21b25ad66dea 100644
--- a/packages/camera/camera/android/build.gradle
+++ b/packages/camera/camera/android/build.gradle
@@ -49,7 +49,7 @@ android {
 dependencies {
     compileOnly 'androidx.annotation:annotation:1.1.0'
     testImplementation 'junit:junit:4.12'
-    testImplementation 'org.mockito:mockito-inline:3.5.13'
+    testImplementation 'org.mockito:mockito-inline:3.11.0'
     testImplementation 'androidx.test:core:1.3.0'
     testImplementation 'org.robolectric:robolectric:4.3'
 }
diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/CameraRegionUtils.java b/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/CameraRegionUtils.java
new file mode 100644
index 000000000000..bc03aae27a03
--- /dev/null
+++ b/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/CameraRegionUtils.java
@@ -0,0 +1,138 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package io.flutter.plugins.camera;
+
+import android.annotation.TargetApi;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.params.MeteringRectangle;
+import android.os.Build;
+import android.util.Size;
+import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
+import java.util.Arrays;
+
+/**
+ * Utility class offering functions to calculate values regarding the camera boundaries.
+ *
+ * <p>The functions are used to calculate focus and exposure settings.
+ */
+public final class CameraRegionUtils {
+
+  /**
+   * Obtains the boundaries for the currently active camera, that can be used for calculating
+   * MeteringRectangle instances required for setting focus or exposure settings.
+   *
+   * @param cameraProperties - Collection of the characteristics for the current camera device.
+   * @param requestBuilder - The request builder for the current capture request.
+   * @return The boundaries for the current camera device.
+   */
+  public static Size getCameraBoundaries(
+      @NonNull CameraProperties cameraProperties, @NonNull CaptureRequest.Builder requestBuilder) {
+    // No distortion correction support
+    if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.P
+        && supportsDistortionCorrection(cameraProperties)) {
+      // Get the current distortion correction mode
+      Integer distortionCorrectionMode =
+          requestBuilder.get(CaptureRequest.DISTORTION_CORRECTION_MODE);
+
+      // Return the correct boundaries depending on the mode
+      android.graphics.Rect rect;
+      if (distortionCorrectionMode == null
+          || distortionCorrectionMode == CaptureRequest.DISTORTION_CORRECTION_MODE_OFF) {
+        rect = cameraProperties.getSensorInfoPreCorrectionActiveArraySize();
+      } else {
+        rect = cameraProperties.getSensorInfoActiveArraySize();
+      }
+
+      // Return camera boundaries
+      return rect == null ? null : new Size(rect.width(), rect.height());
+    } else {
+      // Return camera boundaries
+      return cameraProperties.getSensorInfoPixelArraySize();
+    }
+  }
+
+  /**
+   * Converts a point into a {@link MeteringRectangle} with the supplied coordinates as the centre
+   * point.
+   *
+   * <p>Since the Camera API (due to cross-platform constraints) only accepts a point when
+   * configuring a specific focus or exposure area and Android requires a rectangle to configure
+   * these settings there is a need to convert the point into a rectangle. This method will create
+   * the required rectangle with an arbitrarily size that is a 10th of the current viewport and the
+   * coordinates as the centre point.
+   *
+   * @param boundaries - The camera boundaries to calculate the metering rectangle for.
+   * @param x x - 1 >= coordinate >= 0
+   * @param y y - 1 >= coordinate >= 0
+   * @return The dimensions of the metering rectangle based on the supplied coordinates and
+   *     boundaries.
+   */
+  public static MeteringRectangle convertPointToMeteringRectangle(
+      @NonNull Size boundaries, double x, double y) {
+    assert (boundaries.getWidth() > 0 && boundaries.getHeight() > 0);
+    assert (x >= 0 && x <= 1);
+    assert (y >= 0 && y <= 1);
+
+    // Interpolate the target coordinate
+    int targetX = (int) Math.round(x * ((double) (boundaries.getWidth() - 1)));
+    int targetY = (int) Math.round(y * ((double) (boundaries.getHeight() - 1)));
+    // Since the Camera API only allows Determine the dimensions of the metering rectangle (10th of
+    // the viewport)
+    int targetWidth = (int) Math.round(((double) boundaries.getWidth()) / 10d);
+    int targetHeight = (int) Math.round(((double) boundaries.getHeight()) / 10d);
+    // Adjust target coordinate to represent top-left corner of metering rectangle
+    targetX -= targetWidth / 2;
+    targetY -= targetHeight / 2;
+    // Adjust target coordinate as to not fall out of bounds
+    if (targetX < 0) targetX = 0;
+    if (targetY < 0) targetY = 0;
+    int maxTargetX = boundaries.getWidth() - 1 - targetWidth;
+    int maxTargetY = boundaries.getHeight() - 1 - targetHeight;
+    if (targetX > maxTargetX) targetX = maxTargetX;
+    if (targetY > maxTargetY) targetY = maxTargetY;
+
+    // Build the metering rectangle
+    return MeteringRectangleFactory.create(targetX, targetY, targetWidth, targetHeight, 1);
+  }
+
+  @TargetApi(Build.VERSION_CODES.P)
+  private static boolean supportsDistortionCorrection(CameraProperties cameraProperties) {
+    int[] availableDistortionCorrectionModes =
+        cameraProperties.getDistortionCorrectionAvailableModes();
+    if (availableDistortionCorrectionModes == null) {
+      availableDistortionCorrectionModes = new int[0];
+    }
+    long nonOffModesSupported =
+        Arrays.stream(availableDistortionCorrectionModes)
+            .filter((value) -> value != CaptureRequest.DISTORTION_CORRECTION_MODE_OFF)
+            .count();
+    return nonOffModesSupported > 0;
+  }
+
+  /** Factory class that assists in creating a {@link MeteringRectangle} instance. */
+  static class MeteringRectangleFactory {
+    /**
+     * Creates a new instance of the {@link MeteringRectangle} class.
+     *
+     * <p>This method is visible for testing purposes only and should never be used outside this *
+     * class.
+     *
+     * @param x coordinate >= 0
+     * @param y coordinate >= 0
+     * @param width width >= 0
+     * @param height height >= 0
+     * @param meteringWeight weight between {@value MeteringRectangle#METERING_WEIGHT_MIN} and
+     *     {@value MeteringRectangle#METERING_WEIGHT_MAX} inclusively
+     * @return new instance of the {@link MeteringRectangle} class.
+     * @throws IllegalArgumentException if any of the parameters were negative
+     */
+    @VisibleForTesting
+    public static MeteringRectangle create(
+        int x, int y, int width, int height, int meteringWeight) {
+      return new MeteringRectangle(x, y, width, height, meteringWeight);
+    }
+  }
+}
diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/exposurepoint/ExposurePointFeature.java b/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/exposurepoint/ExposurePointFeature.java
index f729d33c8528..905c9922f600 100644
--- a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/exposurepoint/ExposurePointFeature.java
+++ b/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/exposurepoint/ExposurePointFeature.java
@@ -6,28 +6,36 @@
 
 import android.hardware.camera2.CaptureRequest;
 import android.hardware.camera2.params.MeteringRectangle;
-import android.util.Log;
-import androidx.annotation.NonNull;
+import android.util.Size;
 import io.flutter.plugins.camera.CameraProperties;
+import io.flutter.plugins.camera.CameraRegionUtils;
 import io.flutter.plugins.camera.features.CameraFeature;
 import io.flutter.plugins.camera.features.Point;
-import io.flutter.plugins.camera.types.CameraRegions;
 
 /** Exposure point controls where in the frame exposure metering will come from. */
 public class ExposurePointFeature extends CameraFeature<Point> {
 
-  private final CameraRegions cameraRegions;
-  private Point currentSetting = new Point(0.0, 0.0);
+  private Size cameraBoundaries;
+  private Point exposurePoint;
+  private MeteringRectangle exposureRectangle;
 
   /**
    * Creates a new instance of the {@link ExposurePointFeature}.
    *
    * @param cameraProperties Collection of the characteristics for the current camera device.
-   * @param cameraRegions Utility class to assist in calculating exposure boundaries.
    */
-  public ExposurePointFeature(CameraProperties cameraProperties, CameraRegions cameraRegions) {
+  public ExposurePointFeature(CameraProperties cameraProperties) {
     super(cameraProperties);
-    this.cameraRegions = cameraRegions;
+  }
+
+  /**
+   * Sets the camera boundaries that are required for the exposure point feature to function.
+   *
+   * @param cameraBoundaries - The camera boundaries to set.
+   */
+  public void setCameraBoundaries(Size cameraBoundaries) {
+    this.cameraBoundaries = cameraBoundaries;
+    this.buildExposureRectangle();
   }
 
   @Override
@@ -37,23 +45,19 @@ public String getDebugName() {
 
   @Override
   public Point getValue() {
-    return currentSetting;
+    return exposurePoint;
   }
 
   @Override
-  public void setValue(@NonNull Point value) {
-    this.currentSetting = value;
-
-    if (value.x == null || value.y == null) {
-      cameraRegions.resetAutoExposureMeteringRectangle();
-    } else {
-      cameraRegions.setAutoExposureMeteringRectangleFromPoint(value.x, value.y);
-    }
+  public void setValue(Point value) {
+    this.exposurePoint = value == null || value.x == null || value.y == null ? null : value;
+    this.buildExposureRectangle();
   }
 
   // Whether or not this camera can set the exposure point.
   @Override
   public boolean checkIsSupported() {
+    if (cameraBoundaries == null) return false;
     Integer supportedRegions = cameraProperties.getControlMaxRegionsAutoExposure();
     return supportedRegions != null && supportedRegions > 0;
   }
@@ -63,16 +67,21 @@ public void updateBuilder(CaptureRequest.Builder requestBuilder) {
     if (!checkIsSupported()) {
       return;
     }
-
-    MeteringRectangle aeRect = null;
-    try {
-      aeRect = cameraRegions.getAEMeteringRectangle();
-    } catch (Exception e) {
-      Log.w("Camera", "Unable to retrieve the Auto Exposure metering rectangle.", e);
-    }
-
     requestBuilder.set(
         CaptureRequest.CONTROL_AE_REGIONS,
-        aeRect == null ? null : new MeteringRectangle[] {aeRect});
+        exposureRectangle == null ? null : new MeteringRectangle[] {exposureRectangle});
+  }
+
+  private void buildExposureRectangle() {
+    if (!checkIsSupported()) {
+      return;
+    }
+    if (this.exposurePoint == null) {
+      this.exposureRectangle = null;
+    } else {
+      this.exposureRectangle =
+          CameraRegionUtils.convertPointToMeteringRectangle(
+              this.cameraBoundaries, this.exposurePoint.x, this.exposurePoint.y);
+    }
   }
 }
diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/focuspoint/FocusPointFeature.java b/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/focuspoint/FocusPointFeature.java
new file mode 100644
index 000000000000..8f2941646b4a
--- /dev/null
+++ b/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/focuspoint/FocusPointFeature.java
@@ -0,0 +1,87 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package io.flutter.plugins.camera.features.focuspoint;
+
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.params.MeteringRectangle;
+import android.util.Size;
+import io.flutter.plugins.camera.CameraProperties;
+import io.flutter.plugins.camera.CameraRegionUtils;
+import io.flutter.plugins.camera.features.CameraFeature;
+import io.flutter.plugins.camera.features.Point;
+
+/** Focus point controls where in the frame focus will come from. */
+public class FocusPointFeature extends CameraFeature<Point> {
+
+  private Size cameraBoundaries;
+  private Point focusPoint;
+  private MeteringRectangle focusRectangle;
+
+  /**
+   * Creates a new instance of the {@link FocusPointFeature}.
+   *
+   * @param cameraProperties Collection of the characteristics for the current camera device.
+   */
+  public FocusPointFeature(CameraProperties cameraProperties) {
+    super(cameraProperties);
+  }
+
+  /**
+   * Sets the camera boundaries that are required for the focus point feature to function.
+   *
+   * @param cameraBoundaries - The camera boundaries to set.
+   */
+  public void setCameraBoundaries(Size cameraBoundaries) {
+    this.cameraBoundaries = cameraBoundaries;
+    this.buildFocusRectangle();
+  }
+
+  @Override
+  public String getDebugName() {
+    return "FocusPointFeature";
+  }
+
+  @Override
+  public Point getValue() {
+    return focusPoint;
+  }
+
+  @Override
+  public void setValue(Point value) {
+    this.focusPoint = value == null || value.x == null || value.y == null ? null : value;
+    this.buildFocusRectangle();
+  }
+
+  // Whether or not this camera can set the exposure point.
+  @Override
+  public boolean checkIsSupported() {
+    if (cameraBoundaries == null) return false;
+    Integer supportedRegions = cameraProperties.getControlMaxRegionsAutoFocus();
+    return supportedRegions != null && supportedRegions > 0;
+  }
+
+  @Override
+  public void updateBuilder(CaptureRequest.Builder requestBuilder) {
+    if (!checkIsSupported()) {
+      return;
+    }
+    requestBuilder.set(
+        CaptureRequest.CONTROL_AF_REGIONS,
+        focusRectangle == null ? null : new MeteringRectangle[] {focusRectangle});
+  }
+
+  private void buildFocusRectangle() {
+    if (!checkIsSupported()) {
+      return;
+    }
+    if (this.focusPoint == null) {
+      this.focusRectangle = null;
+    } else {
+      this.focusRectangle =
+          CameraRegionUtils.convertPointToMeteringRectangle(
+              this.cameraBoundaries, this.focusPoint.x, this.focusPoint.y);
+    }
+  }
+}
diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/types/CameraRegions.java b/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/types/CameraRegions.java
deleted file mode 100644
index b86241e78d29..000000000000
--- a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/types/CameraRegions.java
+++ /dev/null
@@ -1,199 +0,0 @@
-// Copyright 2013 The Flutter Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package io.flutter.plugins.camera.types;
-
-import android.annotation.TargetApi;
-import android.hardware.camera2.CaptureRequest;
-import android.hardware.camera2.params.MeteringRectangle;
-import android.os.Build;
-import android.util.Size;
-import androidx.annotation.NonNull;
-import io.flutter.plugins.camera.CameraProperties;
-import java.util.Arrays;
-
-/**
- * Utility class that contains information regarding the camera's regions.
- *
- * <p>The regions information is used to calculate focus and exposure settings.
- */
-public final class CameraRegions {
-
-  /** Factory class that assists in creating a {@link CameraRegions} instance. */
-  public static class Factory {
-    /**
-     * Creates a new instance of the {@link CameraRegions} class.
-     *
-     * <p>The {@link CameraProperties} and {@link CaptureRequest.Builder} classed are used to
-     * determine if the device's camera supports distortion correction mode and calculate the
-     * correct boundaries based on the outcome.
-     *
-     * @param cameraProperties Collection of the characteristics for the current camera device.
-     * @param requestBuilder CaptureRequest builder containing current target and surface settings.
-     * @return new instance of the {@link CameraRegions} class.
-     */
-    public static CameraRegions create(
-        @NonNull CameraProperties cameraProperties,
-        @NonNull CaptureRequest.Builder requestBuilder) {
-      Size boundaries;
-
-      // No distortion correction support
-      if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.P
-          && supportsDistortionCorrection(cameraProperties)) {
-        // Get the current distortion correction mode
-        Integer distortionCorrectionMode =
-            requestBuilder.get(CaptureRequest.DISTORTION_CORRECTION_MODE);
-
-        // Return the correct boundaries depending on the mode
-        android.graphics.Rect rect;
-        if (distortionCorrectionMode == null
-            || distortionCorrectionMode == CaptureRequest.DISTORTION_CORRECTION_MODE_OFF) {
-          rect = cameraProperties.getSensorInfoPreCorrectionActiveArraySize();
-        } else {
-          rect = cameraProperties.getSensorInfoActiveArraySize();
-        }
-
-        // Set new region size
-        boundaries = rect == null ? null : new Size(rect.width(), rect.height());
-      } else {
-        boundaries = cameraProperties.getSensorInfoPixelArraySize();
-      }
-
-      // Create new camera regions using new size
-      return new CameraRegions(boundaries);
-    }
-
-    @TargetApi(Build.VERSION_CODES.P)
-    private static boolean supportsDistortionCorrection(CameraProperties cameraProperties) {
-      int[] availableDistortionCorrectionModes =
-          cameraProperties.getDistortionCorrectionAvailableModes();
-      if (availableDistortionCorrectionModes == null) {
-        availableDistortionCorrectionModes = new int[0];
-      }
-      long nonOffModesSupported =
-          Arrays.stream(availableDistortionCorrectionModes)
-              .filter((value) -> value != CaptureRequest.DISTORTION_CORRECTION_MODE_OFF)
-              .count();
-      return nonOffModesSupported > 0;
-    }
-  }
-
-  private final Size boundaries;
-
-  private MeteringRectangle aeMeteringRectangle;
-  private MeteringRectangle afMeteringRectangle;
-
-  /**
-   * Creates a new instance of the {@link CameraRegions} class.
-   *
-   * @param boundaries The area of the image sensor.
-   */
-  CameraRegions(Size boundaries) {
-    assert (boundaries == null || boundaries.getWidth() > 0);
-    assert (boundaries == null || boundaries.getHeight() > 0);
-
-    this.boundaries = boundaries;
-  }
-
-  /**
-   * Gets the {@link MeteringRectangle} on which the auto exposure will be applied.
-   *
-   * @return The {@link MeteringRectangle} on which the auto exposure will be applied.
-   */
-  public MeteringRectangle getAEMeteringRectangle() {
-    return aeMeteringRectangle;
-  }
-
-  /**
-   * Gets the {@link MeteringRectangle} on which the auto focus will be applied.
-   *
-   * @return The {@link MeteringRectangle} on which the auto focus will be applied.
-   */
-  public MeteringRectangle getAFMeteringRectangle() {
-    return afMeteringRectangle;
-  }
-
-  /**
-   * Gets the area of the image sensor.
-   *
-   * <p>If distortion correction is supported the size corresponds to the active pixels after any
-   * geometric distortion correction has been applied. If distortion correction is not supported the
-   * dimensions include the full pixel array, possibly including black calibration pixels.
-   *
-   * @return The area of the image sensor.
-   */
-  public Size getBoundaries() {
-    return this.boundaries;
-  }
-
-  /** Resets the {@link MeteringRectangle} on which the auto exposure will be applied. */
-  public void resetAutoExposureMeteringRectangle() {
-    this.aeMeteringRectangle = null;
-  }
-
-  /**
-   * Sets the coordinates which will form the centre of the exposure rectangle.
-   *
-   * @param x x – coordinate >= 0
-   * @param y y – coordinate >= 0
-   */
-  public void setAutoExposureMeteringRectangleFromPoint(double x, double y) {
-    this.aeMeteringRectangle = convertPointToMeteringRectangle(x, y);
-  }
-
-  /** Resets the {@link MeteringRectangle} on which the auto focus will be applied. */
-  public void resetAutoFocusMeteringRectangle() {
-    this.afMeteringRectangle = null;
-  }
-
-  /**
-   * Sets the coordinates which will form the centre of the focus rectangle.
-   *
-   * @param x x – coordinate >= 0
-   * @param y y – coordinate >= 0
-   */
-  public void setAutoFocusMeteringRectangleFromPoint(double x, double y) {
-    this.afMeteringRectangle = convertPointToMeteringRectangle(x, y);
-  }
-
-  /**
-   * Converts a point into a {@link MeteringRectangle} with the supplied coordinates as the centre
-   * point.
-   *
-   * <p>Since the Camera API (due to cross-platform constraints) only accepts a point when
-   * configuring a specific focus or exposure area and Android requires a rectangle to configure
-   * these settings there is a need to convert the point into a rectangle. This method will create
-   * the required rectangle with an arbitrarily size that is a 10th of the current viewport and the
-   * coordinates as the centre point.
-   *
-   * @param x x - coordinate >= 0
-   * @param y y - coordinate >= 0
-   * @return The dimensions of the metering rectangle based on the supplied coordinates.
-   */
-  MeteringRectangle convertPointToMeteringRectangle(double x, double y) {
-    assert (x >= 0 && x <= 1);
-    assert (y >= 0 && y <= 1);
-
-    // Interpolate the target coordinate
-    int targetX = (int) Math.round(x * ((double) (boundaries.getWidth() - 1)));
-    int targetY = (int) Math.round(y * ((double) (boundaries.getHeight() - 1)));
-    // Since the Camera API only allows Determine the dimensions of the metering rectangle (10th of
-    // the viewport)
-    int targetWidth = (int) Math.round(((double) boundaries.getWidth()) / 10d);
-    int targetHeight = (int) Math.round(((double) boundaries.getHeight()) / 10d);
-    // Adjust target coordinate to represent top-left corner of metering rectangle
-    targetX -= targetWidth / 2;
-    targetY -= targetHeight / 2;
-    // Adjust target coordinate as to not fall out of bounds
-    if (targetX < 0) targetX = 0;
-    if (targetY < 0) targetY = 0;
-    int maxTargetX = boundaries.getWidth() - 1 - targetWidth;
-    int maxTargetY = boundaries.getHeight() - 1 - targetHeight;
-    if (targetX > maxTargetX) targetX = maxTargetX;
-    if (targetY > maxTargetY) targetY = maxTargetY;
-
-    // Build the metering rectangle
-    return new MeteringRectangle(targetX, targetY, targetWidth, targetHeight, 1);
-  }
-}
diff --git a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/CameraRegionUtilsTest.java b/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/CameraRegionUtilsTest.java
new file mode 100644
index 000000000000..e2255f2c665c
--- /dev/null
+++ b/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/CameraRegionUtilsTest.java
@@ -0,0 +1,294 @@
+package io.flutter.plugins.camera;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.mockStatic;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.params.MeteringRectangle;
+import android.os.Build;
+import android.util.Size;
+import io.flutter.plugins.camera.utils.TestUtils;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.MockedStatic;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+public class CameraRegionUtilsTest {
+
+  Size mockCameraBoundaries;
+
+  @Before
+  public void setUp() {
+    this.mockCameraBoundaries = mock(Size.class);
+    when(this.mockCameraBoundaries.getWidth()).thenReturn(100);
+    when(this.mockCameraBoundaries.getHeight()).thenReturn(100);
+  }
+
+  @Test
+  public void
+      getCameraBoundaries_should_return_sensor_info_pixel_array_size_when_running_pre_android_p() {
+    updateSdkVersion(Build.VERSION_CODES.O_MR1);
+
+    try {
+      CameraProperties mockCameraProperties = mock(CameraProperties.class);
+      CaptureRequest.Builder mockBuilder = mock(CaptureRequest.Builder.class);
+      when(mockCameraProperties.getSensorInfoPixelArraySize()).thenReturn(mockCameraBoundaries);
+
+      Size result = CameraRegionUtils.getCameraBoundaries(mockCameraProperties, mockBuilder);
+
+      assertEquals(mockCameraBoundaries, result);
+      verify(mockCameraProperties, never()).getSensorInfoPreCorrectionActiveArraySize();
+      verify(mockCameraProperties, never()).getSensorInfoActiveArraySize();
+    } finally {
+      updateSdkVersion(0);
+    }
+  }
+
+  @Test
+  public void
+      getCameraBoundaries_should_return_sensor_info_pixel_array_size_when_distortion_correction_is_null() {
+    updateSdkVersion(Build.VERSION_CODES.P);
+
+    try {
+      CameraProperties mockCameraProperties = mock(CameraProperties.class);
+      CaptureRequest.Builder mockBuilder = mock(CaptureRequest.Builder.class);
+
+      when(mockCameraProperties.getDistortionCorrectionAvailableModes()).thenReturn(null);
+      when(mockCameraProperties.getSensorInfoPixelArraySize()).thenReturn(mockCameraBoundaries);
+
+      Size result = CameraRegionUtils.getCameraBoundaries(mockCameraProperties, mockBuilder);
+
+      assertEquals(mockCameraBoundaries, result);
+      verify(mockCameraProperties, never()).getSensorInfoPreCorrectionActiveArraySize();
+      verify(mockCameraProperties, never()).getSensorInfoActiveArraySize();
+    } finally {
+      updateSdkVersion(0);
+    }
+  }
+
+  @Test
+  public void
+      getCameraBoundaries_should_return_sensor_info_pixel_array_size_when_distortion_correction_is_off() {
+    updateSdkVersion(Build.VERSION_CODES.P);
+
+    try {
+      CameraProperties mockCameraProperties = mock(CameraProperties.class);
+      CaptureRequest.Builder mockBuilder = mock(CaptureRequest.Builder.class);
+
+      when(mockCameraProperties.getDistortionCorrectionAvailableModes())
+          .thenReturn(new int[] {CaptureRequest.DISTORTION_CORRECTION_MODE_OFF});
+      when(mockCameraProperties.getSensorInfoPixelArraySize()).thenReturn(mockCameraBoundaries);
+
+      Size result = CameraRegionUtils.getCameraBoundaries(mockCameraProperties, mockBuilder);
+
+      assertEquals(mockCameraBoundaries, result);
+      verify(mockCameraProperties, never()).getSensorInfoPreCorrectionActiveArraySize();
+      verify(mockCameraProperties, never()).getSensorInfoActiveArraySize();
+    } finally {
+      updateSdkVersion(0);
+    }
+  }
+
+  @Test
+  public void
+      getCameraBoundaries_should_return_info_pre_correction_active_array_size_when_distortion_correction_mode_is_set_to_null() {
+    updateSdkVersion(Build.VERSION_CODES.P);
+
+    try {
+      CameraProperties mockCameraProperties = mock(CameraProperties.class);
+      CaptureRequest.Builder mockBuilder = mock(CaptureRequest.Builder.class);
+
+      when(mockCameraProperties.getDistortionCorrectionAvailableModes())
+          .thenReturn(
+              new int[] {
+                CaptureRequest.DISTORTION_CORRECTION_MODE_OFF,
+                CaptureRequest.DISTORTION_CORRECTION_MODE_FAST
+              });
+
+      when(mockBuilder.get(CaptureRequest.DISTORTION_CORRECTION_MODE)).thenReturn(null);
+      when(mockCameraProperties.getSensorInfoPreCorrectionActiveArraySize()).thenReturn(null);
+
+      Size result = CameraRegionUtils.getCameraBoundaries(mockCameraProperties, mockBuilder);
+
+      assertNull(result);
+      verify(mockCameraProperties, never()).getSensorInfoPixelArraySize();
+      verify(mockCameraProperties, never()).getSensorInfoActiveArraySize();
+    } finally {
+      updateSdkVersion(0);
+    }
+  }
+
+  @Test
+  public void
+      getCameraBoundaries_should_return_info_pre_correction_active_array_size_when_distortion_correction_mode_is_set_to_off() {
+    updateSdkVersion(Build.VERSION_CODES.P);
+
+    try {
+      CameraProperties mockCameraProperties = mock(CameraProperties.class);
+      CaptureRequest.Builder mockBuilder = mock(CaptureRequest.Builder.class);
+
+      when(mockCameraProperties.getDistortionCorrectionAvailableModes())
+          .thenReturn(
+              new int[] {
+                CaptureRequest.DISTORTION_CORRECTION_MODE_OFF,
+                CaptureRequest.DISTORTION_CORRECTION_MODE_FAST
+              });
+
+      when(mockBuilder.get(CaptureRequest.DISTORTION_CORRECTION_MODE))
+          .thenReturn(CaptureRequest.DISTORTION_CORRECTION_MODE_OFF);
+      when(mockCameraProperties.getSensorInfoPreCorrectionActiveArraySize()).thenReturn(null);
+
+      Size result = CameraRegionUtils.getCameraBoundaries(mockCameraProperties, mockBuilder);
+
+      assertNull(result);
+      verify(mockCameraProperties, never()).getSensorInfoPixelArraySize();
+      verify(mockCameraProperties, never()).getSensorInfoActiveArraySize();
+    } finally {
+      updateSdkVersion(0);
+    }
+  }
+
+  @Test
+  public void
+      getCameraBoundaries_should_return_sensor_info_active_array_size_when_distortion_correction_mode_is_set() {
+    updateSdkVersion(Build.VERSION_CODES.P);
+
+    try {
+      CameraProperties mockCameraProperties = mock(CameraProperties.class);
+      CaptureRequest.Builder mockBuilder = mock(CaptureRequest.Builder.class);
+
+      when(mockCameraProperties.getDistortionCorrectionAvailableModes())
+          .thenReturn(
+              new int[] {
+                CaptureRequest.DISTORTION_CORRECTION_MODE_OFF,
+                CaptureRequest.DISTORTION_CORRECTION_MODE_FAST
+              });
+
+      when(mockBuilder.get(CaptureRequest.DISTORTION_CORRECTION_MODE))
+          .thenReturn(CaptureRequest.DISTORTION_CORRECTION_MODE_FAST);
+      when(mockCameraProperties.getSensorInfoActiveArraySize()).thenReturn(null);
+
+      Size result = CameraRegionUtils.getCameraBoundaries(mockCameraProperties, mockBuilder);
+
+      assertNull(result);
+      verify(mockCameraProperties, never()).getSensorInfoPixelArraySize();
+      verify(mockCameraProperties, never()).getSensorInfoPreCorrectionActiveArraySize();
+    } finally {
+      updateSdkVersion(0);
+    }
+  }
+
+  @Test(expected = AssertionError.class)
+  public void getMeteringRectangleForPoint_should_throw_for_x_upper_bound() {
+    CameraRegionUtils.convertPointToMeteringRectangle(this.mockCameraBoundaries, 1.5, 0);
+  }
+
+  @Test(expected = AssertionError.class)
+  public void getMeteringRectangleForPoint_should_throw_for_x_lower_bound() {
+    CameraRegionUtils.convertPointToMeteringRectangle(this.mockCameraBoundaries, -0.5, 0);
+  }
+
+  @Test(expected = AssertionError.class)
+  public void getMeteringRectangleForPoint_should_throw_for_y_upper_bound() {
+    CameraRegionUtils.convertPointToMeteringRectangle(this.mockCameraBoundaries, 0, 1.5);
+  }
+
+  @Test(expected = AssertionError.class)
+  public void getMeteringRectangleForPoint_should_throw_for_y_lower_bound() {
+    CameraRegionUtils.convertPointToMeteringRectangle(this.mockCameraBoundaries, 0, -0.5);
+  }
+
+  @Test
+  public void getMeteringRectangleForPoint_should_return_valid_MeteringRectangle() {
+    try (MockedStatic<CameraRegionUtils.MeteringRectangleFactory> mockedMeteringRectangleFactory =
+        mockStatic(CameraRegionUtils.MeteringRectangleFactory.class)) {
+
+      mockedMeteringRectangleFactory
+          .when(
+              () ->
+                  CameraRegionUtils.MeteringRectangleFactory.create(
+                      anyInt(), anyInt(), anyInt(), anyInt(), anyInt()))
+          .thenAnswer(
+              new Answer<MeteringRectangle>() {
+                @Override
+                public MeteringRectangle answer(InvocationOnMock createInvocation)
+                    throws Throwable {
+                  MeteringRectangle mockMeteringRectangle = mock(MeteringRectangle.class);
+                  when(mockMeteringRectangle.getX()).thenReturn(createInvocation.getArgument(0));
+                  when(mockMeteringRectangle.getY()).thenReturn(createInvocation.getArgument(1));
+                  when(mockMeteringRectangle.getWidth())
+                      .thenReturn(createInvocation.getArgument(2));
+                  when(mockMeteringRectangle.getHeight())
+                      .thenReturn(createInvocation.getArgument(3));
+                  when(mockMeteringRectangle.getMeteringWeight())
+                      .thenReturn(createInvocation.getArgument(4));
+                  when(mockMeteringRectangle.equals(any()))
+                      .thenAnswer(
+                          new Answer<Boolean>() {
+                            @Override
+                            public Boolean answer(InvocationOnMock equalsInvocation)
+                                throws Throwable {
+                              MeteringRectangle otherMockMeteringRectangle =
+                                  equalsInvocation.getArgument(0);
+                              return mockMeteringRectangle.getX()
+                                      == otherMockMeteringRectangle.getX()
+                                  && mockMeteringRectangle.getY()
+                                      == otherMockMeteringRectangle.getY()
+                                  && mockMeteringRectangle.getWidth()
+                                      == otherMockMeteringRectangle.getWidth()
+                                  && mockMeteringRectangle.getHeight()
+                                      == otherMockMeteringRectangle.getHeight()
+                                  && mockMeteringRectangle.getMeteringWeight()
+                                      == otherMockMeteringRectangle.getMeteringWeight();
+                            }
+                          });
+                  return mockMeteringRectangle;
+                }
+              });
+
+      MeteringRectangle r;
+      // Center
+      r = CameraRegionUtils.convertPointToMeteringRectangle(this.mockCameraBoundaries, 0.5, 0.5);
+      assertTrue(CameraRegionUtils.MeteringRectangleFactory.create(45, 45, 10, 10, 1).equals(r));
+
+      // Top left
+      r = CameraRegionUtils.convertPointToMeteringRectangle(this.mockCameraBoundaries, 0.0, 0.0);
+      assertTrue(CameraRegionUtils.MeteringRectangleFactory.create(0, 0, 10, 10, 1).equals(r));
+
+      // Bottom right
+      r = CameraRegionUtils.convertPointToMeteringRectangle(this.mockCameraBoundaries, 1.0, 1.0);
+      assertTrue(CameraRegionUtils.MeteringRectangleFactory.create(89, 89, 10, 10, 1).equals(r));
+
+      // Top left
+      r = CameraRegionUtils.convertPointToMeteringRectangle(this.mockCameraBoundaries, 0.0, 1.0);
+      assertTrue(CameraRegionUtils.MeteringRectangleFactory.create(0, 89, 10, 10, 1).equals(r));
+
+      // Top right
+      r = CameraRegionUtils.convertPointToMeteringRectangle(this.mockCameraBoundaries, 1.0, 0.0);
+      assertTrue(CameraRegionUtils.MeteringRectangleFactory.create(89, 0, 10, 10, 1).equals(r));
+    }
+  }
+
+  @Test(expected = AssertionError.class)
+  public void getMeteringRectangleForPoint_should_throw_for_0_width_boundary() {
+    new io.flutter.plugins.camera.CameraRegions(new Size(0, 50));
+  }
+
+  @Test(expected = AssertionError.class)
+  public void getMeteringRectangleForPoint_should_throw_for_0_height_boundary() {
+    new io.flutter.plugins.camera.CameraRegions(new Size(100, 0));
+  }
+
+  private static void updateSdkVersion(int version) {
+    TestUtils.setFinalStatic(Build.VERSION.class, "SDK_INT", version);
+  }
+}
diff --git a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/exposurepoint/ExposurePointFeatureTest.java b/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/exposurepoint/ExposurePointFeatureTest.java
index 0aedc59ef635..1c38affa28cc 100644
--- a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/exposurepoint/ExposurePointFeatureTest.java
+++ b/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/exposurepoint/ExposurePointFeatureTest.java
@@ -6,9 +6,9 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
@@ -17,41 +17,36 @@
 
 import android.hardware.camera2.CaptureRequest;
 import android.hardware.camera2.params.MeteringRectangle;
+import android.util.Size;
 import io.flutter.plugins.camera.CameraProperties;
+import io.flutter.plugins.camera.CameraRegionUtils;
 import io.flutter.plugins.camera.features.Point;
-import io.flutter.plugins.camera.types.CameraRegions;
 import org.junit.Test;
+import org.mockito.MockedStatic;
+import org.mockito.Mockito;
 
 public class ExposurePointFeatureTest {
   @Test
   public void getDebugName_should_return_the_name_of_the_feature() {
     CameraProperties mockCameraProperties = mock(CameraProperties.class);
-    CameraRegions mockCameraRegions = mock(CameraRegions.class);
-    ExposurePointFeature exposurePointFeature =
-        new ExposurePointFeature(mockCameraProperties, mockCameraRegions);
+    CameraRegionUtils mockCameraRegions = mock(CameraRegionUtils.class);
+    ExposurePointFeature exposurePointFeature = new ExposurePointFeature(mockCameraProperties);
 
     assertEquals("ExposurePointFeature", exposurePointFeature.getDebugName());
   }
 
   @Test
-  public void getValue_should_return_default_point_if_not_set() {
+  public void getValue_should_return_null_if_not_set() {
     CameraProperties mockCameraProperties = mock(CameraProperties.class);
-    CameraRegions mockCameraRegions = mock(CameraRegions.class);
-    ExposurePointFeature exposurePointFeature =
-        new ExposurePointFeature(mockCameraProperties, mockCameraRegions);
-    Point expectedPoint = new Point(0.0, 0.0);
+    ExposurePointFeature exposurePointFeature = new ExposurePointFeature(mockCameraProperties);
     Point actualPoint = exposurePointFeature.getValue();
-
-    assertEquals(expectedPoint.x, actualPoint.x);
-    assertEquals(expectedPoint.y, actualPoint.y);
+    assertNull(exposurePointFeature.getValue());
   }
 
   @Test
   public void getValue_should_echo_the_set_value() {
     CameraProperties mockCameraProperties = mock(CameraProperties.class);
-    CameraRegions mockCameraRegions = mock(CameraRegions.class);
-    ExposurePointFeature exposurePointFeature =
-        new ExposurePointFeature(mockCameraProperties, mockCameraRegions);
+    ExposurePointFeature exposurePointFeature = new ExposurePointFeature(mockCameraProperties);
     Point expectedPoint = new Point(0.0, 0.0);
 
     exposurePointFeature.setValue(expectedPoint);
@@ -63,45 +58,113 @@ public void getValue_should_echo_the_set_value() {
   @Test
   public void setValue_should_reset_point_when_x_coord_is_null() {
     CameraProperties mockCameraProperties = mock(CameraProperties.class);
-    CameraRegions mockCameraRegions = mock(CameraRegions.class);
-    ExposurePointFeature exposurePointFeature =
-        new ExposurePointFeature(mockCameraProperties, mockCameraRegions);
+    ExposurePointFeature exposurePointFeature = new ExposurePointFeature(mockCameraProperties);
 
     exposurePointFeature.setValue(new Point(null, 0.0));
 
-    verify(mockCameraRegions, times(1)).resetAutoExposureMeteringRectangle();
+    assertNull(exposurePointFeature.getValue());
   }
 
   @Test
   public void setValue_should_reset_point_when_y_coord_is_null() {
     CameraProperties mockCameraProperties = mock(CameraProperties.class);
-    CameraRegions mockCameraRegions = mock(CameraRegions.class);
-    ExposurePointFeature exposurePointFeature =
-        new ExposurePointFeature(mockCameraProperties, mockCameraRegions);
+    ExposurePointFeature exposurePointFeature = new ExposurePointFeature(mockCameraProperties);
 
     exposurePointFeature.setValue(new Point(0.0, null));
 
-    verify(mockCameraRegions, times(1)).resetAutoExposureMeteringRectangle();
+    assertNull(exposurePointFeature.getValue());
   }
 
   @Test
-  public void setValue_should_reset_point_when_valid_coords_are_supplied() {
+  public void setValue_should_set_point_when_valid_coords_are_supplied() {
     CameraProperties mockCameraProperties = mock(CameraProperties.class);
-    CameraRegions mockCameraRegions = mock(CameraRegions.class);
-    ExposurePointFeature exposurePointFeature =
-        new ExposurePointFeature(mockCameraProperties, mockCameraRegions);
+    ExposurePointFeature exposurePointFeature = new ExposurePointFeature(mockCameraProperties);
     Point point = new Point(0.0, 0.0);
 
     exposurePointFeature.setValue(point);
 
-    verify(mockCameraRegions, times(1)).setAutoExposureMeteringRectangleFromPoint(point.x, point.y);
+    assertEquals(point, exposurePointFeature.getValue());
+  }
+
+  @Test
+  public void
+      setValue_should_determine_metering_rectangle_when_valid_boundaries_and_coords_are_supplied() {
+    CameraProperties mockCameraProperties = mock(CameraProperties.class);
+    when(mockCameraProperties.getControlMaxRegionsAutoExposure()).thenReturn(1);
+    ExposurePointFeature exposurePointFeature = new ExposurePointFeature(mockCameraProperties);
+    Size mockedCameraBoundaries = mock(Size.class);
+    exposurePointFeature.setCameraBoundaries(mockedCameraBoundaries);
+
+    try (MockedStatic<CameraRegionUtils> mockedCameraRegionUtils =
+        Mockito.mockStatic(CameraRegionUtils.class)) {
+
+      exposurePointFeature.setValue(new Point(0.5, 0.5));
+
+      mockedCameraRegionUtils.verify(
+          () -> CameraRegionUtils.convertPointToMeteringRectangle(mockedCameraBoundaries, 0.5, 0.5),
+          times(1));
+    }
+  }
+
+  @Test
+  public void setValue_should_not_determine_metering_rectangle_when_no_valid_boundaries_are_set() {
+    CameraProperties mockCameraProperties = mock(CameraProperties.class);
+    when(mockCameraProperties.getControlMaxRegionsAutoExposure()).thenReturn(1);
+    ExposurePointFeature exposurePointFeature = new ExposurePointFeature(mockCameraProperties);
+
+    try (MockedStatic<CameraRegionUtils> mockedCameraRegionUtils =
+        Mockito.mockStatic(CameraRegionUtils.class)) {
+
+      exposurePointFeature.setValue(new Point(0.5, 0.5));
+
+      mockedCameraRegionUtils.verifyNoInteractions();
+    }
+  }
+
+  @Test
+  public void setValue_should_not_determine_metering_rectangle_when_null_coords_are_set() {
+    CameraProperties mockCameraProperties = mock(CameraProperties.class);
+    when(mockCameraProperties.getControlMaxRegionsAutoExposure()).thenReturn(1);
+    ExposurePointFeature exposurePointFeature = new ExposurePointFeature(mockCameraProperties);
+    Size mockedCameraBoundaries = mock(Size.class);
+    exposurePointFeature.setCameraBoundaries(mockedCameraBoundaries);
+
+    try (MockedStatic<CameraRegionUtils> mockedCameraRegionUtils =
+        Mockito.mockStatic(CameraRegionUtils.class)) {
+
+      exposurePointFeature.setValue(null);
+      exposurePointFeature.setValue(new Point(null, 0.5));
+      exposurePointFeature.setValue(new Point(0.5, null));
+
+      mockedCameraRegionUtils.verifyNoInteractions();
+    }
+  }
+
+  @Test
+  public void
+      setCameraBoundaries_should_determine_metering_rectangle_when_valid_boundaries_and_coords_are_supplied() {
+    CameraProperties mockCameraProperties = mock(CameraProperties.class);
+    when(mockCameraProperties.getControlMaxRegionsAutoExposure()).thenReturn(1);
+    ExposurePointFeature exposurePointFeature = new ExposurePointFeature(mockCameraProperties);
+    exposurePointFeature.setValue(new Point(0.5, 0.5));
+    Size mockedCameraBoundaries = mock(Size.class);
+
+    try (MockedStatic<CameraRegionUtils> mockedCameraRegionUtils =
+        Mockito.mockStatic(CameraRegionUtils.class)) {
+
+      exposurePointFeature.setCameraBoundaries(mockedCameraBoundaries);
+
+      mockedCameraRegionUtils.verify(
+          () -> CameraRegionUtils.convertPointToMeteringRectangle(mockedCameraBoundaries, 0.5, 0.5),
+          times(1));
+    }
   }
 
   @Test
   public void checkIsSupported_should_return_false_when_max_regions_is_null() {
     CameraProperties mockCameraProperties = mock(CameraProperties.class);
-    ExposurePointFeature exposurePointFeature =
-        new ExposurePointFeature(mockCameraProperties, null);
+    ExposurePointFeature exposurePointFeature = new ExposurePointFeature(mockCameraProperties);
+    exposurePointFeature.setCameraBoundaries(new Size(100, 100));
 
     when(mockCameraProperties.getControlMaxRegionsAutoExposure()).thenReturn(null);
 
@@ -111,8 +174,8 @@ public void checkIsSupported_should_return_false_when_max_regions_is_null() {
   @Test
   public void checkIsSupported_should_return_false_when_max_regions_is_zero() {
     CameraProperties mockCameraProperties = mock(CameraProperties.class);
-    ExposurePointFeature exposurePointFeature =
-        new ExposurePointFeature(mockCameraProperties, null);
+    ExposurePointFeature exposurePointFeature = new ExposurePointFeature(mockCameraProperties);
+    exposurePointFeature.setCameraBoundaries(new Size(100, 100));
 
     when(mockCameraProperties.getControlMaxRegionsAutoExposure()).thenReturn(0);
 
@@ -122,8 +185,8 @@ public void checkIsSupported_should_return_false_when_max_regions_is_zero() {
   @Test
   public void checkIsSupported_should_return_true_when_max_regions_is_bigger_then_zero() {
     CameraProperties mockCameraProperties = mock(CameraProperties.class);
-    ExposurePointFeature exposurePointFeature =
-        new ExposurePointFeature(mockCameraProperties, null);
+    ExposurePointFeature exposurePointFeature = new ExposurePointFeature(mockCameraProperties);
+    exposurePointFeature.setCameraBoundaries(new Size(100, 100));
 
     when(mockCameraProperties.getControlMaxRegionsAutoExposure()).thenReturn(1);
 
@@ -133,64 +196,75 @@ public void checkIsSupported_should_return_true_when_max_regions_is_bigger_then_
   @Test
   public void updateBuilder_should_return_when_checkIsSupported_is_false() {
     CameraProperties mockCameraProperties = mock(CameraProperties.class);
-    CameraRegions mockCameraRegions = mock(CameraRegions.class);
-    ExposurePointFeature exposurePointFeature =
-        new ExposurePointFeature(mockCameraProperties, mockCameraRegions);
+    CaptureRequest.Builder mockCaptureRequestBuilder = mock(CaptureRequest.Builder.class);
+    ExposurePointFeature exposurePointFeature = new ExposurePointFeature(mockCameraProperties);
 
     when(mockCameraProperties.getControlMaxRegionsAutoExposure()).thenReturn(0);
 
-    exposurePointFeature.updateBuilder(null);
+    exposurePointFeature.updateBuilder(mockCaptureRequestBuilder);
 
-    verify(mockCameraRegions, never()).getAEMeteringRectangle();
+    verify(mockCaptureRequestBuilder, never()).set(any(), any());
   }
 
   @Test
-  public void updateBuilder_should_set_ae_regions_to_null_when_ae_metering_rectangle_is_null() {
+  public void
+      updateBuilder_should_set_metering_rectangle_when_valid_boundaries_and_coords_are_supplied() {
     CameraProperties mockCameraProperties = mock(CameraProperties.class);
-    CameraRegions mockCameraRegions = mock(CameraRegions.class);
-    CaptureRequest.Builder mockBuilder = mock(CaptureRequest.Builder.class);
-    ExposurePointFeature exposurePointFeature =
-        new ExposurePointFeature(mockCameraProperties, mockCameraRegions);
-
     when(mockCameraProperties.getControlMaxRegionsAutoExposure()).thenReturn(1);
-    when(mockCameraRegions.getAEMeteringRectangle()).thenReturn(null);
-
-    exposurePointFeature.updateBuilder(mockBuilder);
-
-    verify(mockBuilder, times(1)).set(CaptureRequest.CONTROL_AE_REGIONS, null);
+    CaptureRequest.Builder mockCaptureRequestBuilder = mock(CaptureRequest.Builder.class);
+    ExposurePointFeature exposurePointFeature = new ExposurePointFeature(mockCameraProperties);
+    Size mockedCameraBoundaries = mock(Size.class);
+    MeteringRectangle mockedMeteringRectangle = mock(MeteringRectangle.class);
+
+    try (MockedStatic<CameraRegionUtils> mockedCameraRegionUtils =
+        Mockito.mockStatic(CameraRegionUtils.class)) {
+      mockedCameraRegionUtils
+          .when(
+              () ->
+                  CameraRegionUtils.convertPointToMeteringRectangle(
+                      mockedCameraBoundaries, 0.5, 0.5))
+          .thenReturn(mockedMeteringRectangle);
+      exposurePointFeature.setCameraBoundaries(mockedCameraBoundaries);
+      exposurePointFeature.setValue(new Point(0.5, 0.5));
+
+      exposurePointFeature.updateBuilder(mockCaptureRequestBuilder);
+    }
+
+    verify(mockCaptureRequestBuilder, times(1))
+        .set(CaptureRequest.CONTROL_AE_REGIONS, new MeteringRectangle[] {mockedMeteringRectangle});
   }
 
   @Test
-  public void updateBuilder_should_set_ae_regions_with_metering_rectangle() {
+  public void
+      updateBuilder_should_not_set_metering_rectangle_when_no_valid_boundaries_are_supplied() {
     CameraProperties mockCameraProperties = mock(CameraProperties.class);
-    CameraRegions mockCameraRegions = mock(CameraRegions.class);
-    CaptureRequest.Builder mockBuilder = mock(CaptureRequest.Builder.class);
-    ExposurePointFeature exposurePointFeature =
-        new ExposurePointFeature(mockCameraProperties, mockCameraRegions);
-    MeteringRectangle meteringRectangle = new MeteringRectangle(0, 0, 0, 0, 0);
-
     when(mockCameraProperties.getControlMaxRegionsAutoExposure()).thenReturn(1);
-    when(mockCameraRegions.getAEMeteringRectangle()).thenReturn(meteringRectangle);
+    CaptureRequest.Builder mockCaptureRequestBuilder = mock(CaptureRequest.Builder.class);
+    ExposurePointFeature exposurePointFeature = new ExposurePointFeature(mockCameraProperties);
+    MeteringRectangle mockedMeteringRectangle = mock(MeteringRectangle.class);
+
+    exposurePointFeature.setValue(new Point(0.5, 0.5));
 
-    exposurePointFeature.updateBuilder(mockBuilder);
+    exposurePointFeature.updateBuilder(mockCaptureRequestBuilder);
 
-    verify(mockBuilder, times(1))
-        .set(eq(CaptureRequest.CONTROL_AE_REGIONS), any(MeteringRectangle[].class));
+    verify(mockCaptureRequestBuilder, never()).set(any(), any());
   }
 
   @Test
-  public void updateBuilder_should_silently_fail_when_exception_occurs() {
+  public void dateBuilder_should_not_set_metering_rectangle_when_no_valid_coords_are_supplied() {
     CameraProperties mockCameraProperties = mock(CameraProperties.class);
-    CameraRegions mockCameraRegions = mock(CameraRegions.class);
-    CaptureRequest.Builder mockBuilder = mock(CaptureRequest.Builder.class);
-    ExposurePointFeature exposurePointFeature =
-        new ExposurePointFeature(mockCameraProperties, mockCameraRegions);
-
     when(mockCameraProperties.getControlMaxRegionsAutoExposure()).thenReturn(1);
-    when(mockCameraRegions.getAEMeteringRectangle()).thenThrow(new IllegalArgumentException());
-
-    exposurePointFeature.updateBuilder(mockBuilder);
-
-    verify(mockBuilder, times(1)).set(CaptureRequest.CONTROL_AE_REGIONS, null);
+    CaptureRequest.Builder mockCaptureRequestBuilder = mock(CaptureRequest.Builder.class);
+    ExposurePointFeature exposurePointFeature = new ExposurePointFeature(mockCameraProperties);
+
+    exposurePointFeature.setValue(null);
+    exposurePointFeature.updateBuilder(mockCaptureRequestBuilder);
+    verify(mockCaptureRequestBuilder, never()).set(any(), any());
+    exposurePointFeature.setValue(new Point(0d, null));
+    exposurePointFeature.updateBuilder(mockCaptureRequestBuilder);
+    verify(mockCaptureRequestBuilder, never()).set(any(), any());
+    exposurePointFeature.setValue(new Point(null, 0d));
+    exposurePointFeature.updateBuilder(mockCaptureRequestBuilder);
+    verify(mockCaptureRequestBuilder, never()).set(any(), any());
   }
 }
diff --git a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/focuspoint/FocusPointFeatureTest.java b/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/focuspoint/FocusPointFeatureTest.java
new file mode 100644
index 000000000000..ec733e64aaba
--- /dev/null
+++ b/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/focuspoint/FocusPointFeatureTest.java
@@ -0,0 +1,270 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package io.flutter.plugins.camera.features.focuspoint;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.params.MeteringRectangle;
+import android.util.Size;
+import io.flutter.plugins.camera.CameraProperties;
+import io.flutter.plugins.camera.CameraRegionUtils;
+import io.flutter.plugins.camera.features.Point;
+import org.junit.Test;
+import org.mockito.MockedStatic;
+import org.mockito.Mockito;
+
+public class FocusPointFeatureTest {
+  @Test
+  public void getDebugName_should_return_the_name_of_the_feature() {
+    CameraProperties mockCameraProperties = mock(CameraProperties.class);
+    CameraRegionUtils mockCameraRegions = mock(CameraRegionUtils.class);
+    FocusPointFeature focusPointFeature = new FocusPointFeature(mockCameraProperties);
+
+    assertEquals("FocusPointFeature", focusPointFeature.getDebugName());
+  }
+
+  @Test
+  public void getValue_should_return_null_if_not_set() {
+    CameraProperties mockCameraProperties = mock(CameraProperties.class);
+    FocusPointFeature focusPointFeature = new FocusPointFeature(mockCameraProperties);
+    Point actualPoint = focusPointFeature.getValue();
+    assertNull(focusPointFeature.getValue());
+  }
+
+  @Test
+  public void getValue_should_echo_the_set_value() {
+    CameraProperties mockCameraProperties = mock(CameraProperties.class);
+    FocusPointFeature focusPointFeature = new FocusPointFeature(mockCameraProperties);
+    Point expectedPoint = new Point(0.0, 0.0);
+
+    focusPointFeature.setValue(expectedPoint);
+    Point actualPoint = focusPointFeature.getValue();
+
+    assertEquals(expectedPoint, actualPoint);
+  }
+
+  @Test
+  public void setValue_should_reset_point_when_x_coord_is_null() {
+    CameraProperties mockCameraProperties = mock(CameraProperties.class);
+    FocusPointFeature focusPointFeature = new FocusPointFeature(mockCameraProperties);
+
+    focusPointFeature.setValue(new Point(null, 0.0));
+
+    assertNull(focusPointFeature.getValue());
+  }
+
+  @Test
+  public void setValue_should_reset_point_when_y_coord_is_null() {
+    CameraProperties mockCameraProperties = mock(CameraProperties.class);
+    FocusPointFeature focusPointFeature = new FocusPointFeature(mockCameraProperties);
+
+    focusPointFeature.setValue(new Point(0.0, null));
+
+    assertNull(focusPointFeature.getValue());
+  }
+
+  @Test
+  public void setValue_should_set_point_when_valid_coords_are_supplied() {
+    CameraProperties mockCameraProperties = mock(CameraProperties.class);
+    FocusPointFeature focusPointFeature = new FocusPointFeature(mockCameraProperties);
+    Point point = new Point(0.0, 0.0);
+
+    focusPointFeature.setValue(point);
+
+    assertEquals(point, focusPointFeature.getValue());
+  }
+
+  @Test
+  public void
+      setValue_should_determine_metering_rectangle_when_valid_boundaries_and_coords_are_supplied() {
+    CameraProperties mockCameraProperties = mock(CameraProperties.class);
+    when(mockCameraProperties.getControlMaxRegionsAutoFocus()).thenReturn(1);
+    FocusPointFeature focusPointFeature = new FocusPointFeature(mockCameraProperties);
+    Size mockedCameraBoundaries = mock(Size.class);
+    focusPointFeature.setCameraBoundaries(mockedCameraBoundaries);
+
+    try (MockedStatic<CameraRegionUtils> mockedCameraRegionUtils =
+        Mockito.mockStatic(CameraRegionUtils.class)) {
+
+      focusPointFeature.setValue(new Point(0.5, 0.5));
+
+      mockedCameraRegionUtils.verify(
+          () -> CameraRegionUtils.convertPointToMeteringRectangle(mockedCameraBoundaries, 0.5, 0.5),
+          times(1));
+    }
+  }
+
+  @Test
+  public void setValue_should_not_determine_metering_rectangle_when_no_valid_boundaries_are_set() {
+    CameraProperties mockCameraProperties = mock(CameraProperties.class);
+    when(mockCameraProperties.getControlMaxRegionsAutoFocus()).thenReturn(1);
+    FocusPointFeature focusPointFeature = new FocusPointFeature(mockCameraProperties);
+
+    try (MockedStatic<CameraRegionUtils> mockedCameraRegionUtils =
+        Mockito.mockStatic(CameraRegionUtils.class)) {
+
+      focusPointFeature.setValue(new Point(0.5, 0.5));
+
+      mockedCameraRegionUtils.verifyNoInteractions();
+    }
+  }
+
+  @Test
+  public void setValue_should_not_determine_metering_rectangle_when_null_coords_are_set() {
+    CameraProperties mockCameraProperties = mock(CameraProperties.class);
+    when(mockCameraProperties.getControlMaxRegionsAutoFocus()).thenReturn(1);
+    FocusPointFeature focusPointFeature = new FocusPointFeature(mockCameraProperties);
+    Size mockedCameraBoundaries = mock(Size.class);
+    focusPointFeature.setCameraBoundaries(mockedCameraBoundaries);
+
+    try (MockedStatic<CameraRegionUtils> mockedCameraRegionUtils =
+        Mockito.mockStatic(CameraRegionUtils.class)) {
+
+      focusPointFeature.setValue(null);
+      focusPointFeature.setValue(new Point(null, 0.5));
+      focusPointFeature.setValue(new Point(0.5, null));
+
+      mockedCameraRegionUtils.verifyNoInteractions();
+    }
+  }
+
+  @Test
+  public void
+      setCameraBoundaries_should_determine_metering_rectangle_when_valid_boundaries_and_coords_are_supplied() {
+    CameraProperties mockCameraProperties = mock(CameraProperties.class);
+    when(mockCameraProperties.getControlMaxRegionsAutoFocus()).thenReturn(1);
+    FocusPointFeature focusPointFeature = new FocusPointFeature(mockCameraProperties);
+    focusPointFeature.setValue(new Point(0.5, 0.5));
+    Size mockedCameraBoundaries = mock(Size.class);
+
+    try (MockedStatic<CameraRegionUtils> mockedCameraRegionUtils =
+        Mockito.mockStatic(CameraRegionUtils.class)) {
+
+      focusPointFeature.setCameraBoundaries(mockedCameraBoundaries);
+
+      mockedCameraRegionUtils.verify(
+          () -> CameraRegionUtils.convertPointToMeteringRectangle(mockedCameraBoundaries, 0.5, 0.5),
+          times(1));
+    }
+  }
+
+  @Test
+  public void checkIsSupported_should_return_false_when_max_regions_is_null() {
+    CameraProperties mockCameraProperties = mock(CameraProperties.class);
+    FocusPointFeature focusPointFeature = new FocusPointFeature(mockCameraProperties);
+    focusPointFeature.setCameraBoundaries(new Size(100, 100));
+
+    when(mockCameraProperties.getControlMaxRegionsAutoFocus()).thenReturn(null);
+
+    assertFalse(focusPointFeature.checkIsSupported());
+  }
+
+  @Test
+  public void checkIsSupported_should_return_false_when_max_regions_is_zero() {
+    CameraProperties mockCameraProperties = mock(CameraProperties.class);
+    FocusPointFeature focusPointFeature = new FocusPointFeature(mockCameraProperties);
+    focusPointFeature.setCameraBoundaries(new Size(100, 100));
+
+    when(mockCameraProperties.getControlMaxRegionsAutoFocus()).thenReturn(0);
+
+    assertFalse(focusPointFeature.checkIsSupported());
+  }
+
+  @Test
+  public void checkIsSupported_should_return_true_when_max_regions_is_bigger_then_zero() {
+    CameraProperties mockCameraProperties = mock(CameraProperties.class);
+    FocusPointFeature focusPointFeature = new FocusPointFeature(mockCameraProperties);
+    focusPointFeature.setCameraBoundaries(new Size(100, 100));
+
+    when(mockCameraProperties.getControlMaxRegionsAutoFocus()).thenReturn(1);
+
+    assertTrue(focusPointFeature.checkIsSupported());
+  }
+
+  @Test
+  public void updateBuilder_should_return_when_checkIsSupported_is_false() {
+    CameraProperties mockCameraProperties = mock(CameraProperties.class);
+    CaptureRequest.Builder mockCaptureRequestBuilder = mock(CaptureRequest.Builder.class);
+    FocusPointFeature focusPointFeature = new FocusPointFeature(mockCameraProperties);
+
+    when(mockCameraProperties.getControlMaxRegionsAutoFocus()).thenReturn(0);
+
+    focusPointFeature.updateBuilder(mockCaptureRequestBuilder);
+
+    verify(mockCaptureRequestBuilder, never()).set(any(), any());
+  }
+
+  @Test
+  public void
+      updateBuilder_should_set_metering_rectangle_when_valid_boundaries_and_coords_are_supplied() {
+    CameraProperties mockCameraProperties = mock(CameraProperties.class);
+    when(mockCameraProperties.getControlMaxRegionsAutoFocus()).thenReturn(1);
+    CaptureRequest.Builder mockCaptureRequestBuilder = mock(CaptureRequest.Builder.class);
+    FocusPointFeature focusPointFeature = new FocusPointFeature(mockCameraProperties);
+    Size mockedCameraBoundaries = mock(Size.class);
+    MeteringRectangle mockedMeteringRectangle = mock(MeteringRectangle.class);
+
+    try (MockedStatic<CameraRegionUtils> mockedCameraRegionUtils =
+        Mockito.mockStatic(CameraRegionUtils.class)) {
+      mockedCameraRegionUtils
+          .when(
+              () ->
+                  CameraRegionUtils.convertPointToMeteringRectangle(
+                      mockedCameraBoundaries, 0.5, 0.5))
+          .thenReturn(mockedMeteringRectangle);
+      focusPointFeature.setCameraBoundaries(mockedCameraBoundaries);
+      focusPointFeature.setValue(new Point(0.5, 0.5));
+
+      focusPointFeature.updateBuilder(mockCaptureRequestBuilder);
+    }
+
+    verify(mockCaptureRequestBuilder, times(1))
+        .set(CaptureRequest.CONTROL_AE_REGIONS, new MeteringRectangle[] {mockedMeteringRectangle});
+  }
+
+  @Test
+  public void
+      updateBuilder_should_not_set_metering_rectangle_when_no_valid_boundaries_are_supplied() {
+    CameraProperties mockCameraProperties = mock(CameraProperties.class);
+    when(mockCameraProperties.getControlMaxRegionsAutoFocus()).thenReturn(1);
+    CaptureRequest.Builder mockCaptureRequestBuilder = mock(CaptureRequest.Builder.class);
+    FocusPointFeature focusPointFeature = new FocusPointFeature(mockCameraProperties);
+    MeteringRectangle mockedMeteringRectangle = mock(MeteringRectangle.class);
+
+    focusPointFeature.setValue(new Point(0.5, 0.5));
+
+    focusPointFeature.updateBuilder(mockCaptureRequestBuilder);
+
+    verify(mockCaptureRequestBuilder, never()).set(any(), any());
+  }
+
+  @Test
+  public void dateBuilder_should_not_set_metering_rectangle_when_no_valid_coords_are_supplied() {
+    CameraProperties mockCameraProperties = mock(CameraProperties.class);
+    when(mockCameraProperties.getControlMaxRegionsAutoFocus()).thenReturn(1);
+    CaptureRequest.Builder mockCaptureRequestBuilder = mock(CaptureRequest.Builder.class);
+    FocusPointFeature focusPointFeature = new FocusPointFeature(mockCameraProperties);
+
+    focusPointFeature.setValue(null);
+    focusPointFeature.updateBuilder(mockCaptureRequestBuilder);
+    verify(mockCaptureRequestBuilder, never()).set(any(), any());
+    focusPointFeature.setValue(new Point(0d, null));
+    focusPointFeature.updateBuilder(mockCaptureRequestBuilder);
+    verify(mockCaptureRequestBuilder, never()).set(any(), any());
+    focusPointFeature.setValue(new Point(null, 0d));
+    focusPointFeature.updateBuilder(mockCaptureRequestBuilder);
+    verify(mockCaptureRequestBuilder, never()).set(any(), any());
+  }
+}
diff --git a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/types/CameraRegionsFactoryTest.java b/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/types/CameraRegionsFactoryTest.java
deleted file mode 100644
index 5fa0c2c4a2a4..000000000000
--- a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/types/CameraRegionsFactoryTest.java
+++ /dev/null
@@ -1,201 +0,0 @@
-// Copyright 2013 The Flutter Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package io.flutter.plugins.camera.types;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.hardware.camera2.CaptureRequest;
-import android.os.Build.VERSION;
-import android.os.Build.VERSION_CODES;
-import android.util.Size;
-import io.flutter.plugins.camera.CameraProperties;
-import io.flutter.plugins.camera.utils.TestUtils;
-import org.junit.Before;
-import org.junit.Test;
-
-public class CameraRegionsFactoryTest {
-  private Size mockSize;
-
-  @Before
-  public void before() {
-    mockSize = mock(Size.class);
-
-    when(mockSize.getHeight()).thenReturn(640);
-    when(mockSize.getWidth()).thenReturn(480);
-  }
-
-  @Test
-  public void
-      create_should_initialize_with_sensor_info_pixel_array_size_when_running_pre_android_p() {
-    updateSdkVersion(VERSION_CODES.O_MR1);
-
-    try {
-      CameraProperties mockCameraProperties = mock(CameraProperties.class);
-      CaptureRequest.Builder mockBuilder = mock(CaptureRequest.Builder.class);
-
-      when(mockCameraProperties.getSensorInfoPixelArraySize()).thenReturn(mockSize);
-
-      CameraRegions cameraRegions = CameraRegions.Factory.create(mockCameraProperties, mockBuilder);
-
-      assertEquals(mockSize, cameraRegions.getBoundaries());
-      verify(mockCameraProperties, never()).getSensorInfoPreCorrectionActiveArraySize();
-      verify(mockCameraProperties, never()).getSensorInfoActiveArraySize();
-    } finally {
-      updateSdkVersion(0);
-    }
-  }
-
-  @Test
-  public void
-      create_should_initialize_with_sensor_info_pixel_array_size_when_distortion_correction_is_null() {
-    updateSdkVersion(VERSION_CODES.P);
-
-    try {
-      CameraProperties mockCameraProperties = mock(CameraProperties.class);
-      CaptureRequest.Builder mockBuilder = mock(CaptureRequest.Builder.class);
-
-      when(mockCameraProperties.getDistortionCorrectionAvailableModes()).thenReturn(null);
-      when(mockCameraProperties.getSensorInfoPixelArraySize()).thenReturn(mockSize);
-
-      CameraRegions cameraRegions = CameraRegions.Factory.create(mockCameraProperties, mockBuilder);
-
-      assertEquals(mockSize, cameraRegions.getBoundaries());
-      verify(mockCameraProperties, never()).getSensorInfoPreCorrectionActiveArraySize();
-      verify(mockCameraProperties, never()).getSensorInfoActiveArraySize();
-    } finally {
-      updateSdkVersion(0);
-    }
-  }
-
-  @Test
-  public void
-      create_should_initialize_with_sensor_info_pixel_array_size_when_distortion_correction_is_off() {
-    updateSdkVersion(VERSION_CODES.P);
-
-    try {
-      CameraProperties mockCameraProperties = mock(CameraProperties.class);
-      CaptureRequest.Builder mockBuilder = mock(CaptureRequest.Builder.class);
-
-      when(mockCameraProperties.getDistortionCorrectionAvailableModes())
-          .thenReturn(new int[] {CaptureRequest.DISTORTION_CORRECTION_MODE_OFF});
-      when(mockCameraProperties.getSensorInfoPixelArraySize()).thenReturn(mockSize);
-
-      CameraRegions cameraRegions = CameraRegions.Factory.create(mockCameraProperties, mockBuilder);
-
-      assertEquals(mockSize, cameraRegions.getBoundaries());
-      verify(mockCameraProperties, never()).getSensorInfoPreCorrectionActiveArraySize();
-      verify(mockCameraProperties, never()).getSensorInfoActiveArraySize();
-    } finally {
-      updateSdkVersion(0);
-    }
-  }
-
-  @Test
-  public void
-      create_should_initialize_with_sensor_info_pre_correction_active_array_size_when_distortion_correction_mode_is_set_to_null() {
-    updateSdkVersion(VERSION_CODES.P);
-
-    try {
-      CameraProperties mockCameraProperties = mock(CameraProperties.class);
-      CaptureRequest.Builder mockBuilder = mock(CaptureRequest.Builder.class);
-
-      when(mockCameraProperties.getDistortionCorrectionAvailableModes())
-          .thenReturn(
-              new int[] {
-                CaptureRequest.DISTORTION_CORRECTION_MODE_OFF,
-                CaptureRequest.DISTORTION_CORRECTION_MODE_FAST
-              });
-
-      when(mockBuilder.get(CaptureRequest.DISTORTION_CORRECTION_MODE)).thenReturn(null);
-      when(mockCameraProperties.getSensorInfoPreCorrectionActiveArraySize()).thenReturn(null);
-
-      CameraRegions cameraRegions = CameraRegions.Factory.create(mockCameraProperties, mockBuilder);
-
-      assertNull(cameraRegions.getBoundaries());
-      verify(mockCameraProperties, never()).getSensorInfoPixelArraySize();
-      verify(mockCameraProperties, never()).getSensorInfoActiveArraySize();
-    } finally {
-      updateSdkVersion(0);
-    }
-  }
-
-  @Test
-  public void
-      create_should_initialize_with_sensor_info_pre_correction_active_array_size_when_distortion_correction_mode_is_set_off() {
-    updateSdkVersion(VERSION_CODES.P);
-
-    try {
-      CameraProperties mockCameraProperties = mock(CameraProperties.class);
-      CaptureRequest.Builder mockBuilder = mock(CaptureRequest.Builder.class);
-
-      when(mockCameraProperties.getDistortionCorrectionAvailableModes())
-          .thenReturn(
-              new int[] {
-                CaptureRequest.DISTORTION_CORRECTION_MODE_OFF,
-                CaptureRequest.DISTORTION_CORRECTION_MODE_FAST
-              });
-
-      when(mockBuilder.get(CaptureRequest.DISTORTION_CORRECTION_MODE))
-          .thenReturn(CaptureRequest.DISTORTION_CORRECTION_MODE_OFF);
-      when(mockCameraProperties.getSensorInfoPreCorrectionActiveArraySize()).thenReturn(null);
-
-      CameraRegions cameraRegions = CameraRegions.Factory.create(mockCameraProperties, mockBuilder);
-
-      assertNull(cameraRegions.getBoundaries());
-      verify(mockCameraProperties, never()).getSensorInfoPixelArraySize();
-      verify(mockCameraProperties, never()).getSensorInfoActiveArraySize();
-    } finally {
-      updateSdkVersion(0);
-    }
-  }
-
-  @Test
-  public void
-      ctor_should_initialize_with_sensor_info_active_array_size_when_distortion_correction_mode_is_set() {
-    updateSdkVersion(VERSION_CODES.P);
-
-    try {
-      CameraProperties mockCameraProperties = mock(CameraProperties.class);
-      CaptureRequest.Builder mockBuilder = mock(CaptureRequest.Builder.class);
-
-      when(mockCameraProperties.getDistortionCorrectionAvailableModes())
-          .thenReturn(
-              new int[] {
-                CaptureRequest.DISTORTION_CORRECTION_MODE_OFF,
-                CaptureRequest.DISTORTION_CORRECTION_MODE_FAST
-              });
-
-      when(mockBuilder.get(CaptureRequest.DISTORTION_CORRECTION_MODE))
-          .thenReturn(CaptureRequest.DISTORTION_CORRECTION_MODE_FAST);
-      when(mockCameraProperties.getSensorInfoActiveArraySize()).thenReturn(null);
-
-      CameraRegions cameraRegions = CameraRegions.Factory.create(mockCameraProperties, mockBuilder);
-
-      assertNull(cameraRegions.getBoundaries());
-      verify(mockCameraProperties, never()).getSensorInfoPixelArraySize();
-      verify(mockCameraProperties, never()).getSensorInfoPreCorrectionActiveArraySize();
-    } finally {
-      updateSdkVersion(0);
-    }
-  }
-
-  @Test
-  public void getBoundaries_should_return_null_if_not_set() {
-    CameraProperties mockCameraProperties = mock(CameraProperties.class);
-    CaptureRequest.Builder mockBuilder = mock(CaptureRequest.Builder.class);
-    CameraRegions cameraRegions = CameraRegions.Factory.create(mockCameraProperties, mockBuilder);
-
-    assertNull(cameraRegions.getBoundaries());
-  }
-
-  private static void updateSdkVersion(int version) {
-    TestUtils.setFinalStatic(VERSION.class, "SDK_INT", version);
-  }
-}
diff --git a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/types/CameraRegionsTest.java b/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/types/CameraRegionsTest.java
deleted file mode 100644
index b760e1e9ca29..000000000000
--- a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/types/CameraRegionsTest.java
+++ /dev/null
@@ -1,114 +0,0 @@
-// Copyright 2013 The Flutter Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package io.flutter.plugins.camera.types;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-
-import android.hardware.camera2.params.MeteringRectangle;
-import android.util.Size;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
-
-@RunWith(RobolectricTestRunner.class)
-public class CameraRegionsTest {
-  io.flutter.plugins.camera.types.CameraRegions cameraRegions;
-
-  @Before
-  public void setUp() {
-    this.cameraRegions = new io.flutter.plugins.camera.types.CameraRegions(new Size(100, 100));
-  }
-
-  @Test(expected = AssertionError.class)
-  public void getMeteringRectangleForPoint_should_throw_for_x_upper_bound() {
-    cameraRegions.convertPointToMeteringRectangle(1.5, 0);
-  }
-
-  @Test(expected = AssertionError.class)
-  public void getMeteringRectangleForPoint_should_throw_for_x_lower_bound() {
-    cameraRegions.convertPointToMeteringRectangle(-0.5, 0);
-  }
-
-  @Test(expected = AssertionError.class)
-  public void getMeteringRectangleForPoint_should_throw_for_y_upper_bound() {
-    cameraRegions.convertPointToMeteringRectangle(0, 1.5);
-  }
-
-  @Test(expected = AssertionError.class)
-  public void getMeteringRectangleForPoint_should_throw_for_y_lower_bound() {
-    cameraRegions.convertPointToMeteringRectangle(0, -0.5);
-  }
-
-  @Test
-  public void getMeteringRectangleForPoint_should_return_valid_MeteringRectangle() {
-    MeteringRectangle r;
-    // Center
-    r = cameraRegions.convertPointToMeteringRectangle(0.5, 0.5);
-    assertEquals(new MeteringRectangle(45, 45, 10, 10, 1), r);
-
-    // Top left
-    r = cameraRegions.convertPointToMeteringRectangle(0.0, 0.0);
-    assertEquals(new MeteringRectangle(0, 0, 10, 10, 1), r);
-
-    // Bottom right
-    r = cameraRegions.convertPointToMeteringRectangle(1.0, 1.0);
-    assertEquals(new MeteringRectangle(89, 89, 10, 10, 1), r);
-
-    // Top left
-    r = cameraRegions.convertPointToMeteringRectangle(0.0, 1.0);
-    assertEquals(new MeteringRectangle(0, 89, 10, 10, 1), r);
-
-    // Top right
-    r = cameraRegions.convertPointToMeteringRectangle(1.0, 0.0);
-    assertEquals(new MeteringRectangle(89, 0, 10, 10, 1), r);
-  }
-
-  @Test(expected = AssertionError.class)
-  public void constructor_should_throw_for_0_width_boundary() {
-    new io.flutter.plugins.camera.CameraRegions(new Size(0, 50));
-  }
-
-  @Test(expected = AssertionError.class)
-  public void constructor_should_throw_for_0_height_boundary() {
-    new io.flutter.plugins.camera.CameraRegions(new Size(100, 0));
-  }
-
-  @Test
-  public void setAutoExposureMeteringRectangleFromPoint_should_set_aeMeteringRectangle_for_point() {
-    cameraRegions.setAutoExposureMeteringRectangleFromPoint(0, 0);
-    assertEquals(new MeteringRectangle(0, 0, 10, 10, 1), cameraRegions.getAEMeteringRectangle());
-  }
-
-  @Test
-  public void resetAutoExposureMeteringRectangle_should_reset_aeMeteringRectangle() {
-    io.flutter.plugins.camera.types.CameraRegions cr =
-        new io.flutter.plugins.camera.types.CameraRegions(new Size(100, 50));
-    cr.setAutoExposureMeteringRectangleFromPoint(0, 0);
-    assertNotNull(cr.getAEMeteringRectangle());
-    cr.resetAutoExposureMeteringRectangle();
-    assertNull(cr.getAEMeteringRectangle());
-  }
-
-  @Test
-  public void setAutoFocusMeteringRectangleFromPoint_should_set_afMeteringRectangle_for_point() {
-    io.flutter.plugins.camera.types.CameraRegions cr =
-        new io.flutter.plugins.camera.types.CameraRegions(new Size(100, 50));
-    cr.setAutoFocusMeteringRectangleFromPoint(0, 0);
-    assertEquals(new MeteringRectangle(0, 0, 10, 5, 1), cr.getAFMeteringRectangle());
-  }
-
-  @Test
-  public void resetAutoFocusMeteringRectangle_should_reset_afMeteringRectangle() {
-    io.flutter.plugins.camera.types.CameraRegions cr =
-        new io.flutter.plugins.camera.types.CameraRegions(new Size(100, 50));
-    cr.setAutoFocusMeteringRectangleFromPoint(0, 0);
-    assertNotNull(cr.getAFMeteringRectangle());
-    cr.resetAutoFocusMeteringRectangle();
-    assertNull(cr.getAFMeteringRectangle());
-  }
-}

From 4f169aba29756c7509143fcf7599739bd535ddb6 Mon Sep 17 00:00:00 2001
From: BeMacized <info@bemacized.net>
Date: Tue, 15 Jun 2021 13:56:02 +0200
Subject: [PATCH 2/4] Implemented PR feedback

---
 packages/camera/camera/android/build.gradle   |  2 +-
 .../exposurepoint/ExposurePointFeature.java   |  9 ++---
 .../focuspoint/FocusPointFeature.java         | 13 +++----
 .../plugins/camera/CameraRegionUtilsTest.java |  3 ++
 .../ExposurePointFeatureTest.java             | 35 ++++++++++++-------
 .../focuspoint/FocusPointFeatureTest.java     | 35 ++++++++++++-------
 6 files changed, 62 insertions(+), 35 deletions(-)

diff --git a/packages/camera/camera/android/build.gradle b/packages/camera/camera/android/build.gradle
index 21b25ad66dea..65c6d26edb49 100644
--- a/packages/camera/camera/android/build.gradle
+++ b/packages/camera/camera/android/build.gradle
@@ -49,7 +49,7 @@ android {
 dependencies {
     compileOnly 'androidx.annotation:annotation:1.1.0'
     testImplementation 'junit:junit:4.12'
-    testImplementation 'org.mockito:mockito-inline:3.11.0'
+    testImplementation 'org.mockito:mockito-inline:3.11.1'
     testImplementation 'androidx.test:core:1.3.0'
     testImplementation 'org.robolectric:robolectric:4.3'
 }
diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/exposurepoint/ExposurePointFeature.java b/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/exposurepoint/ExposurePointFeature.java
index 905c9922f600..b98e2666bec2 100644
--- a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/exposurepoint/ExposurePointFeature.java
+++ b/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/exposurepoint/ExposurePointFeature.java
@@ -7,6 +7,7 @@
 import android.hardware.camera2.CaptureRequest;
 import android.hardware.camera2.params.MeteringRectangle;
 import android.util.Size;
+import androidx.annotation.NonNull;
 import io.flutter.plugins.camera.CameraProperties;
 import io.flutter.plugins.camera.CameraRegionUtils;
 import io.flutter.plugins.camera.features.CameraFeature;
@@ -33,7 +34,7 @@ public ExposurePointFeature(CameraProperties cameraProperties) {
    *
    * @param cameraBoundaries - The camera boundaries to set.
    */
-  public void setCameraBoundaries(Size cameraBoundaries) {
+  public void setCameraBoundaries(@NonNull Size cameraBoundaries) {
     this.cameraBoundaries = cameraBoundaries;
     this.buildExposureRectangle();
   }
@@ -57,7 +58,6 @@ public void setValue(Point value) {
   // Whether or not this camera can set the exposure point.
   @Override
   public boolean checkIsSupported() {
-    if (cameraBoundaries == null) return false;
     Integer supportedRegions = cameraProperties.getControlMaxRegionsAutoExposure();
     return supportedRegions != null && supportedRegions > 0;
   }
@@ -73,8 +73,9 @@ public void updateBuilder(CaptureRequest.Builder requestBuilder) {
   }
 
   private void buildExposureRectangle() {
-    if (!checkIsSupported()) {
-      return;
+    if (this.cameraBoundaries == null) {
+      throw new AssertionError(
+          "The cameraBoundaries should be set (using `ExposurePointFeature.setCameraBoundaries(Size)`) before updating the exposure point.");
     }
     if (this.exposurePoint == null) {
       this.exposureRectangle = null;
diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/focuspoint/FocusPointFeature.java b/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/focuspoint/FocusPointFeature.java
index 8f2941646b4a..92fcfa9f1132 100644
--- a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/focuspoint/FocusPointFeature.java
+++ b/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/focuspoint/FocusPointFeature.java
@@ -1,4 +1,4 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
+// Copyright 2013 The Flutter Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,6 +7,7 @@
 import android.hardware.camera2.CaptureRequest;
 import android.hardware.camera2.params.MeteringRectangle;
 import android.util.Size;
+import androidx.annotation.NonNull;
 import io.flutter.plugins.camera.CameraProperties;
 import io.flutter.plugins.camera.CameraRegionUtils;
 import io.flutter.plugins.camera.features.CameraFeature;
@@ -33,7 +34,7 @@ public FocusPointFeature(CameraProperties cameraProperties) {
    *
    * @param cameraBoundaries - The camera boundaries to set.
    */
-  public void setCameraBoundaries(Size cameraBoundaries) {
+  public void setCameraBoundaries(@NonNull Size cameraBoundaries) {
     this.cameraBoundaries = cameraBoundaries;
     this.buildFocusRectangle();
   }
@@ -54,10 +55,9 @@ public void setValue(Point value) {
     this.buildFocusRectangle();
   }
 
-  // Whether or not this camera can set the exposure point.
+  // Whether or not this camera can set the focus point.
   @Override
   public boolean checkIsSupported() {
-    if (cameraBoundaries == null) return false;
     Integer supportedRegions = cameraProperties.getControlMaxRegionsAutoFocus();
     return supportedRegions != null && supportedRegions > 0;
   }
@@ -73,8 +73,9 @@ public void updateBuilder(CaptureRequest.Builder requestBuilder) {
   }
 
   private void buildFocusRectangle() {
-    if (!checkIsSupported()) {
-      return;
+    if (this.cameraBoundaries == null) {
+      throw new AssertionError(
+          "The cameraBoundaries should be set (using `FocusPointFeature.setCameraBoundaries(Size)`) before updating the focus point.");
     }
     if (this.focusPoint == null) {
       this.focusRectangle = null;
diff --git a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/CameraRegionUtilsTest.java b/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/CameraRegionUtilsTest.java
index e2255f2c665c..e248e8adfeb4 100644
--- a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/CameraRegionUtilsTest.java
+++ b/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/CameraRegionUtilsTest.java
@@ -1,3 +1,6 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
 package io.flutter.plugins.camera;
 
 import static org.junit.Assert.assertEquals;
diff --git a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/exposurepoint/ExposurePointFeatureTest.java b/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/exposurepoint/ExposurePointFeatureTest.java
index 1c38affa28cc..4a515c6fd0ec 100644
--- a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/exposurepoint/ExposurePointFeatureTest.java
+++ b/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/exposurepoint/ExposurePointFeatureTest.java
@@ -9,6 +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.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
@@ -21,11 +22,22 @@
 import io.flutter.plugins.camera.CameraProperties;
 import io.flutter.plugins.camera.CameraRegionUtils;
 import io.flutter.plugins.camera.features.Point;
+import org.junit.Before;
 import org.junit.Test;
 import org.mockito.MockedStatic;
 import org.mockito.Mockito;
 
 public class ExposurePointFeatureTest {
+
+  Size mockCameraBoundaries;
+
+  @Before
+  public void setUp() {
+    this.mockCameraBoundaries = mock(Size.class);
+    when(this.mockCameraBoundaries.getWidth()).thenReturn(100);
+    when(this.mockCameraBoundaries.getHeight()).thenReturn(100);
+  }
+
   @Test
   public void getDebugName_should_return_the_name_of_the_feature() {
     CameraProperties mockCameraProperties = mock(CameraProperties.class);
@@ -47,6 +59,7 @@ public void getValue_should_return_null_if_not_set() {
   public void getValue_should_echo_the_set_value() {
     CameraProperties mockCameraProperties = mock(CameraProperties.class);
     ExposurePointFeature exposurePointFeature = new ExposurePointFeature(mockCameraProperties);
+    exposurePointFeature.setCameraBoundaries(this.mockCameraBoundaries);
     Point expectedPoint = new Point(0.0, 0.0);
 
     exposurePointFeature.setValue(expectedPoint);
@@ -59,6 +72,7 @@ public void getValue_should_echo_the_set_value() {
   public void setValue_should_reset_point_when_x_coord_is_null() {
     CameraProperties mockCameraProperties = mock(CameraProperties.class);
     ExposurePointFeature exposurePointFeature = new ExposurePointFeature(mockCameraProperties);
+    exposurePointFeature.setCameraBoundaries(this.mockCameraBoundaries);
 
     exposurePointFeature.setValue(new Point(null, 0.0));
 
@@ -69,6 +83,7 @@ public void setValue_should_reset_point_when_x_coord_is_null() {
   public void setValue_should_reset_point_when_y_coord_is_null() {
     CameraProperties mockCameraProperties = mock(CameraProperties.class);
     ExposurePointFeature exposurePointFeature = new ExposurePointFeature(mockCameraProperties);
+    exposurePointFeature.setCameraBoundaries(this.mockCameraBoundaries);
 
     exposurePointFeature.setValue(new Point(0.0, null));
 
@@ -79,6 +94,7 @@ public void setValue_should_reset_point_when_y_coord_is_null() {
   public void setValue_should_set_point_when_valid_coords_are_supplied() {
     CameraProperties mockCameraProperties = mock(CameraProperties.class);
     ExposurePointFeature exposurePointFeature = new ExposurePointFeature(mockCameraProperties);
+    exposurePointFeature.setCameraBoundaries(this.mockCameraBoundaries);
     Point point = new Point(0.0, 0.0);
 
     exposurePointFeature.setValue(point);
@@ -106,18 +122,15 @@ public void setValue_should_set_point_when_valid_coords_are_supplied() {
     }
   }
 
-  @Test
-  public void setValue_should_not_determine_metering_rectangle_when_no_valid_boundaries_are_set() {
+  @Test(expected = AssertionError.class)
+  public void setValue_should_throw_assertion_error_when_no_valid_boundaries_are_set() {
     CameraProperties mockCameraProperties = mock(CameraProperties.class);
     when(mockCameraProperties.getControlMaxRegionsAutoExposure()).thenReturn(1);
     ExposurePointFeature exposurePointFeature = new ExposurePointFeature(mockCameraProperties);
 
     try (MockedStatic<CameraRegionUtils> mockedCameraRegionUtils =
         Mockito.mockStatic(CameraRegionUtils.class)) {
-
       exposurePointFeature.setValue(new Point(0.5, 0.5));
-
-      mockedCameraRegionUtils.verifyNoInteractions();
     }
   }
 
@@ -146,6 +159,7 @@ public void setValue_should_not_determine_metering_rectangle_when_null_coords_ar
     CameraProperties mockCameraProperties = mock(CameraProperties.class);
     when(mockCameraProperties.getControlMaxRegionsAutoExposure()).thenReturn(1);
     ExposurePointFeature exposurePointFeature = new ExposurePointFeature(mockCameraProperties);
+    exposurePointFeature.setCameraBoundaries(this.mockCameraBoundaries);
     exposurePointFeature.setValue(new Point(0.5, 0.5));
     Size mockedCameraBoundaries = mock(Size.class);
 
@@ -243,28 +257,25 @@ public void updateBuilder_should_return_when_checkIsSupported_is_false() {
     ExposurePointFeature exposurePointFeature = new ExposurePointFeature(mockCameraProperties);
     MeteringRectangle mockedMeteringRectangle = mock(MeteringRectangle.class);
 
-    exposurePointFeature.setValue(new Point(0.5, 0.5));
-
     exposurePointFeature.updateBuilder(mockCaptureRequestBuilder);
 
-    verify(mockCaptureRequestBuilder, never()).set(any(), any());
+    verify(mockCaptureRequestBuilder, times(1)).set(any(), isNull());
   }
 
   @Test
-  public void dateBuilder_should_not_set_metering_rectangle_when_no_valid_coords_are_supplied() {
+  public void updateBuilder_should_not_set_metering_rectangle_when_no_valid_coords_are_supplied() {
     CameraProperties mockCameraProperties = mock(CameraProperties.class);
     when(mockCameraProperties.getControlMaxRegionsAutoExposure()).thenReturn(1);
     CaptureRequest.Builder mockCaptureRequestBuilder = mock(CaptureRequest.Builder.class);
     ExposurePointFeature exposurePointFeature = new ExposurePointFeature(mockCameraProperties);
+    exposurePointFeature.setCameraBoundaries(this.mockCameraBoundaries);
 
     exposurePointFeature.setValue(null);
     exposurePointFeature.updateBuilder(mockCaptureRequestBuilder);
-    verify(mockCaptureRequestBuilder, never()).set(any(), any());
     exposurePointFeature.setValue(new Point(0d, null));
     exposurePointFeature.updateBuilder(mockCaptureRequestBuilder);
-    verify(mockCaptureRequestBuilder, never()).set(any(), any());
     exposurePointFeature.setValue(new Point(null, 0d));
     exposurePointFeature.updateBuilder(mockCaptureRequestBuilder);
-    verify(mockCaptureRequestBuilder, never()).set(any(), any());
+    verify(mockCaptureRequestBuilder, times(3)).set(any(), isNull());
   }
 }
diff --git a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/focuspoint/FocusPointFeatureTest.java b/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/focuspoint/FocusPointFeatureTest.java
index ec733e64aaba..d158336ef235 100644
--- a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/focuspoint/FocusPointFeatureTest.java
+++ b/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/features/focuspoint/FocusPointFeatureTest.java
@@ -9,6 +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.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
@@ -21,11 +22,22 @@
 import io.flutter.plugins.camera.CameraProperties;
 import io.flutter.plugins.camera.CameraRegionUtils;
 import io.flutter.plugins.camera.features.Point;
+import org.junit.Before;
 import org.junit.Test;
 import org.mockito.MockedStatic;
 import org.mockito.Mockito;
 
 public class FocusPointFeatureTest {
+
+  Size mockCameraBoundaries;
+
+  @Before
+  public void setUp() {
+    this.mockCameraBoundaries = mock(Size.class);
+    when(this.mockCameraBoundaries.getWidth()).thenReturn(100);
+    when(this.mockCameraBoundaries.getHeight()).thenReturn(100);
+  }
+
   @Test
   public void getDebugName_should_return_the_name_of_the_feature() {
     CameraProperties mockCameraProperties = mock(CameraProperties.class);
@@ -47,6 +59,7 @@ public void getValue_should_return_null_if_not_set() {
   public void getValue_should_echo_the_set_value() {
     CameraProperties mockCameraProperties = mock(CameraProperties.class);
     FocusPointFeature focusPointFeature = new FocusPointFeature(mockCameraProperties);
+    focusPointFeature.setCameraBoundaries(this.mockCameraBoundaries);
     Point expectedPoint = new Point(0.0, 0.0);
 
     focusPointFeature.setValue(expectedPoint);
@@ -59,6 +72,7 @@ public void getValue_should_echo_the_set_value() {
   public void setValue_should_reset_point_when_x_coord_is_null() {
     CameraProperties mockCameraProperties = mock(CameraProperties.class);
     FocusPointFeature focusPointFeature = new FocusPointFeature(mockCameraProperties);
+    focusPointFeature.setCameraBoundaries(this.mockCameraBoundaries);
 
     focusPointFeature.setValue(new Point(null, 0.0));
 
@@ -69,6 +83,7 @@ public void setValue_should_reset_point_when_x_coord_is_null() {
   public void setValue_should_reset_point_when_y_coord_is_null() {
     CameraProperties mockCameraProperties = mock(CameraProperties.class);
     FocusPointFeature focusPointFeature = new FocusPointFeature(mockCameraProperties);
+    focusPointFeature.setCameraBoundaries(this.mockCameraBoundaries);
 
     focusPointFeature.setValue(new Point(0.0, null));
 
@@ -79,6 +94,7 @@ public void setValue_should_reset_point_when_y_coord_is_null() {
   public void setValue_should_set_point_when_valid_coords_are_supplied() {
     CameraProperties mockCameraProperties = mock(CameraProperties.class);
     FocusPointFeature focusPointFeature = new FocusPointFeature(mockCameraProperties);
+    focusPointFeature.setCameraBoundaries(this.mockCameraBoundaries);
     Point point = new Point(0.0, 0.0);
 
     focusPointFeature.setValue(point);
@@ -106,18 +122,15 @@ public void setValue_should_set_point_when_valid_coords_are_supplied() {
     }
   }
 
-  @Test
-  public void setValue_should_not_determine_metering_rectangle_when_no_valid_boundaries_are_set() {
+  @Test(expected = AssertionError.class)
+  public void setValue_should_throw_assertion_error_when_no_valid_boundaries_are_set() {
     CameraProperties mockCameraProperties = mock(CameraProperties.class);
     when(mockCameraProperties.getControlMaxRegionsAutoFocus()).thenReturn(1);
     FocusPointFeature focusPointFeature = new FocusPointFeature(mockCameraProperties);
 
     try (MockedStatic<CameraRegionUtils> mockedCameraRegionUtils =
         Mockito.mockStatic(CameraRegionUtils.class)) {
-
       focusPointFeature.setValue(new Point(0.5, 0.5));
-
-      mockedCameraRegionUtils.verifyNoInteractions();
     }
   }
 
@@ -146,6 +159,7 @@ public void setValue_should_not_determine_metering_rectangle_when_null_coords_ar
     CameraProperties mockCameraProperties = mock(CameraProperties.class);
     when(mockCameraProperties.getControlMaxRegionsAutoFocus()).thenReturn(1);
     FocusPointFeature focusPointFeature = new FocusPointFeature(mockCameraProperties);
+    focusPointFeature.setCameraBoundaries(this.mockCameraBoundaries);
     focusPointFeature.setValue(new Point(0.5, 0.5));
     Size mockedCameraBoundaries = mock(Size.class);
 
@@ -243,28 +257,25 @@ public void updateBuilder_should_return_when_checkIsSupported_is_false() {
     FocusPointFeature focusPointFeature = new FocusPointFeature(mockCameraProperties);
     MeteringRectangle mockedMeteringRectangle = mock(MeteringRectangle.class);
 
-    focusPointFeature.setValue(new Point(0.5, 0.5));
-
     focusPointFeature.updateBuilder(mockCaptureRequestBuilder);
 
-    verify(mockCaptureRequestBuilder, never()).set(any(), any());
+    verify(mockCaptureRequestBuilder, times(1)).set(any(), isNull());
   }
 
   @Test
-  public void dateBuilder_should_not_set_metering_rectangle_when_no_valid_coords_are_supplied() {
+  public void updateBuilder_should_not_set_metering_rectangle_when_no_valid_coords_are_supplied() {
     CameraProperties mockCameraProperties = mock(CameraProperties.class);
     when(mockCameraProperties.getControlMaxRegionsAutoFocus()).thenReturn(1);
     CaptureRequest.Builder mockCaptureRequestBuilder = mock(CaptureRequest.Builder.class);
     FocusPointFeature focusPointFeature = new FocusPointFeature(mockCameraProperties);
+    focusPointFeature.setCameraBoundaries(this.mockCameraBoundaries);
 
     focusPointFeature.setValue(null);
     focusPointFeature.updateBuilder(mockCaptureRequestBuilder);
-    verify(mockCaptureRequestBuilder, never()).set(any(), any());
     focusPointFeature.setValue(new Point(0d, null));
     focusPointFeature.updateBuilder(mockCaptureRequestBuilder);
-    verify(mockCaptureRequestBuilder, never()).set(any(), any());
     focusPointFeature.setValue(new Point(null, 0d));
     focusPointFeature.updateBuilder(mockCaptureRequestBuilder);
-    verify(mockCaptureRequestBuilder, never()).set(any(), any());
+    verify(mockCaptureRequestBuilder, times(3)).set(any(), isNull());
   }
 }

From ba3e96b17c53cd843201915920709c97920b26b3 Mon Sep 17 00:00:00 2001
From: BeMacized <info@bemacized.net>
Date: Tue, 22 Jun 2021 10:24:21 +0200
Subject: [PATCH 3/4] Implemented PR feedback

---
 .../plugins/camera/CameraRegionUtils.java     | 55 ++++++++++---------
 .../exposurepoint/ExposurePointFeature.java   |  2 +-
 2 files changed, 31 insertions(+), 26 deletions(-)

diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/CameraRegionUtils.java b/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/CameraRegionUtils.java
index bc03aae27a03..489c2e27ca35 100644
--- a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/CameraRegionUtils.java
+++ b/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/CameraRegionUtils.java
@@ -30,14 +30,13 @@ public final class CameraRegionUtils {
    */
   public static Size getCameraBoundaries(
       @NonNull CameraProperties cameraProperties, @NonNull CaptureRequest.Builder requestBuilder) {
-    // No distortion correction support
     if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.P
         && supportsDistortionCorrection(cameraProperties)) {
-      // Get the current distortion correction mode
+      // Get the current distortion correction mode.
       Integer distortionCorrectionMode =
           requestBuilder.get(CaptureRequest.DISTORTION_CORRECTION_MODE);
 
-      // Return the correct boundaries depending on the mode
+      // Return the correct boundaries depending on the mode.
       android.graphics.Rect rect;
       if (distortionCorrectionMode == null
           || distortionCorrectionMode == CaptureRequest.DISTORTION_CORRECTION_MODE_OFF) {
@@ -46,27 +45,26 @@ && supportsDistortionCorrection(cameraProperties)) {
         rect = cameraProperties.getSensorInfoActiveArraySize();
       }
 
-      // Return camera boundaries
-      return rect == null ? null : new Size(rect.width(), rect.height());
+      return new Size(rect.width(), rect.height());
     } else {
-      // Return camera boundaries
+      // No distortion correction support.
       return cameraProperties.getSensorInfoPixelArraySize();
     }
   }
 
   /**
-   * Converts a point into a {@link MeteringRectangle} with the supplied coordinates as the centre
+   * Converts a point into a {@link MeteringRectangle} with the supplied coordinates as the center
    * point.
    *
    * <p>Since the Camera API (due to cross-platform constraints) only accepts a point when
    * configuring a specific focus or exposure area and Android requires a rectangle to configure
    * these settings there is a need to convert the point into a rectangle. This method will create
    * the required rectangle with an arbitrarily size that is a 10th of the current viewport and the
-   * coordinates as the centre point.
+   * coordinates as the center point.
    *
    * @param boundaries - The camera boundaries to calculate the metering rectangle for.
-   * @param x x - 1 >= coordinate >= 0
-   * @param y y - 1 >= coordinate >= 0
+   * @param x x - 1 >= coordinate >= 0.
+   * @param y y - 1 >= coordinate >= 0.
    * @return The dimensions of the metering rectangle based on the supplied coordinates and
    *     boundaries.
    */
@@ -76,25 +74,32 @@ public static MeteringRectangle convertPointToMeteringRectangle(
     assert (x >= 0 && x <= 1);
     assert (y >= 0 && y <= 1);
 
-    // Interpolate the target coordinate
+    // Interpolate the target coordinate.
     int targetX = (int) Math.round(x * ((double) (boundaries.getWidth() - 1)));
     int targetY = (int) Math.round(y * ((double) (boundaries.getHeight() - 1)));
-    // Since the Camera API only allows Determine the dimensions of the metering rectangle (10th of
-    // the viewport)
+    // Determine the dimensions of the metering rectangle (10th of the viewport).
     int targetWidth = (int) Math.round(((double) boundaries.getWidth()) / 10d);
     int targetHeight = (int) Math.round(((double) boundaries.getHeight()) / 10d);
-    // Adjust target coordinate to represent top-left corner of metering rectangle
+    // Adjust target coordinate to represent top-left corner of metering rectangle.
     targetX -= targetWidth / 2;
     targetY -= targetHeight / 2;
-    // Adjust target coordinate as to not fall out of bounds
-    if (targetX < 0) targetX = 0;
-    if (targetY < 0) targetY = 0;
+    // Adjust target coordinate as to not fall out of bounds.
+    if (targetX < 0) {
+      targetX = 0;
+    }
+    if (targetY < 0) {
+      targetY = 0;
+    }
     int maxTargetX = boundaries.getWidth() - 1 - targetWidth;
     int maxTargetY = boundaries.getHeight() - 1 - targetHeight;
-    if (targetX > maxTargetX) targetX = maxTargetX;
-    if (targetY > maxTargetY) targetY = maxTargetY;
+    if (targetX > maxTargetX) {
+      targetX = maxTargetX;
+    }
+    if (targetY > maxTargetY) {
+      targetY = maxTargetY;
+    }
 
-    // Build the metering rectangle
+    // Build the metering rectangle.
     return MeteringRectangleFactory.create(targetX, targetY, targetWidth, targetHeight, 1);
   }
 
@@ -120,14 +125,14 @@ static class MeteringRectangleFactory {
      * <p>This method is visible for testing purposes only and should never be used outside this *
      * class.
      *
-     * @param x coordinate >= 0
-     * @param y coordinate >= 0
-     * @param width width >= 0
-     * @param height height >= 0
+     * @param x coordinate >= 0.
+     * @param y coordinate >= 0.
+     * @param width width >= 0.
+     * @param height height >= 0.
      * @param meteringWeight weight between {@value MeteringRectangle#METERING_WEIGHT_MIN} and
      *     {@value MeteringRectangle#METERING_WEIGHT_MAX} inclusively
      * @return new instance of the {@link MeteringRectangle} class.
-     * @throws IllegalArgumentException if any of the parameters were negative
+     * @throws IllegalArgumentException if any of the parameters were negative.
      */
     @VisibleForTesting
     public static MeteringRectangle create(
diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/exposurepoint/ExposurePointFeature.java b/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/exposurepoint/ExposurePointFeature.java
index b98e2666bec2..8c2ee6167846 100644
--- a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/exposurepoint/ExposurePointFeature.java
+++ b/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/exposurepoint/ExposurePointFeature.java
@@ -51,7 +51,7 @@ public Point getValue() {
 
   @Override
   public void setValue(Point value) {
-    this.exposurePoint = value == null || value.x == null || value.y == null ? null : value;
+    this.exposurePoint = (value == null || value.x == null || value.y == null) ? null : value;
     this.buildExposureRectangle();
   }
 

From 25a7bb005980d802faeb6e067434fc20c76939db Mon Sep 17 00:00:00 2001
From: BeMacized <info@bemacized.net>
Date: Wed, 23 Jun 2021 20:06:14 +0200
Subject: [PATCH 4/4] [camera] Fix native android tests

---
 .../plugins/camera/CameraRegionUtils.java     |  20 +++-
 .../plugins/camera/CameraRegionUtilsTest.java | 102 ++++++++++++++----
 2 files changed, 98 insertions(+), 24 deletions(-)

diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/CameraRegionUtils.java b/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/CameraRegionUtils.java
index 489c2e27ca35..ff8a49f1d148 100644
--- a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/CameraRegionUtils.java
+++ b/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/CameraRegionUtils.java
@@ -45,7 +45,7 @@ && supportsDistortionCorrection(cameraProperties)) {
         rect = cameraProperties.getSensorInfoActiveArraySize();
       }
 
-      return new Size(rect.width(), rect.height());
+      return SizeFactory.create(rect.width(), rect.height());
     } else {
       // No distortion correction support.
       return cameraProperties.getSensorInfoPixelArraySize();
@@ -140,4 +140,22 @@ public static MeteringRectangle create(
       return new MeteringRectangle(x, y, width, height, meteringWeight);
     }
   }
+
+  /** Factory class that assists in creating a {@link Size} instance. */
+  static class SizeFactory {
+    /**
+     * Creates a new instance of the {@link Size} class.
+     *
+     * <p>This method is visible for testing purposes only and should never be used outside this *
+     * class.
+     *
+     * @param width width >= 0.
+     * @param height height >= 0.
+     * @return new instance of the {@link Size} class.
+     */
+    @VisibleForTesting
+    public static Size create(int width, int height) {
+      return new Size(width, height);
+    }
+  }
 }
diff --git a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/CameraRegionUtilsTest.java b/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/CameraRegionUtilsTest.java
index e248e8adfeb4..2d65c4e0fc05 100644
--- a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/CameraRegionUtilsTest.java
+++ b/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/CameraRegionUtilsTest.java
@@ -4,7 +4,6 @@
 package io.flutter.plugins.camera;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
@@ -14,6 +13,7 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.graphics.Rect;
 import android.hardware.camera2.CaptureRequest;
 import android.hardware.camera2.params.MeteringRectangle;
 import android.os.Build;
@@ -109,6 +109,9 @@ public void setUp() {
     try {
       CameraProperties mockCameraProperties = mock(CameraProperties.class);
       CaptureRequest.Builder mockBuilder = mock(CaptureRequest.Builder.class);
+      Rect mockSensorInfoPreCorrectionActiveArraySize = mock(Rect.class);
+      when(mockSensorInfoPreCorrectionActiveArraySize.width()).thenReturn(100);
+      when(mockSensorInfoPreCorrectionActiveArraySize.height()).thenReturn(100);
 
       when(mockCameraProperties.getDistortionCorrectionAvailableModes())
           .thenReturn(
@@ -116,15 +119,30 @@ public void setUp() {
                 CaptureRequest.DISTORTION_CORRECTION_MODE_OFF,
                 CaptureRequest.DISTORTION_CORRECTION_MODE_FAST
               });
-
       when(mockBuilder.get(CaptureRequest.DISTORTION_CORRECTION_MODE)).thenReturn(null);
-      when(mockCameraProperties.getSensorInfoPreCorrectionActiveArraySize()).thenReturn(null);
-
-      Size result = CameraRegionUtils.getCameraBoundaries(mockCameraProperties, mockBuilder);
-
-      assertNull(result);
-      verify(mockCameraProperties, never()).getSensorInfoPixelArraySize();
-      verify(mockCameraProperties, never()).getSensorInfoActiveArraySize();
+      when(mockCameraProperties.getSensorInfoPreCorrectionActiveArraySize())
+          .thenReturn(mockSensorInfoPreCorrectionActiveArraySize);
+
+      try (MockedStatic<CameraRegionUtils.SizeFactory> mockedSizeFactory =
+          mockStatic(CameraRegionUtils.SizeFactory.class)) {
+        mockedSizeFactory
+            .when(() -> CameraRegionUtils.SizeFactory.create(anyInt(), anyInt()))
+            .thenAnswer(
+                (Answer<Size>)
+                    invocation -> {
+                      Size mockSize = mock(Size.class);
+                      when(mockSize.getWidth()).thenReturn(invocation.getArgument(0));
+                      when(mockSize.getHeight()).thenReturn(invocation.getArgument(1));
+                      return mockSize;
+                    });
+
+        Size result = CameraRegionUtils.getCameraBoundaries(mockCameraProperties, mockBuilder);
+
+        assertEquals(100, result.getWidth());
+        assertEquals(100, result.getHeight());
+        verify(mockCameraProperties, never()).getSensorInfoPixelArraySize();
+        verify(mockCameraProperties, never()).getSensorInfoActiveArraySize();
+      }
     } finally {
       updateSdkVersion(0);
     }
@@ -138,6 +156,9 @@ public void setUp() {
     try {
       CameraProperties mockCameraProperties = mock(CameraProperties.class);
       CaptureRequest.Builder mockBuilder = mock(CaptureRequest.Builder.class);
+      Rect mockSensorInfoPreCorrectionActiveArraySize = mock(Rect.class);
+      when(mockSensorInfoPreCorrectionActiveArraySize.width()).thenReturn(100);
+      when(mockSensorInfoPreCorrectionActiveArraySize.height()).thenReturn(100);
 
       when(mockCameraProperties.getDistortionCorrectionAvailableModes())
           .thenReturn(
@@ -148,13 +169,29 @@ public void setUp() {
 
       when(mockBuilder.get(CaptureRequest.DISTORTION_CORRECTION_MODE))
           .thenReturn(CaptureRequest.DISTORTION_CORRECTION_MODE_OFF);
-      when(mockCameraProperties.getSensorInfoPreCorrectionActiveArraySize()).thenReturn(null);
-
-      Size result = CameraRegionUtils.getCameraBoundaries(mockCameraProperties, mockBuilder);
-
-      assertNull(result);
-      verify(mockCameraProperties, never()).getSensorInfoPixelArraySize();
-      verify(mockCameraProperties, never()).getSensorInfoActiveArraySize();
+      when(mockCameraProperties.getSensorInfoPreCorrectionActiveArraySize())
+          .thenReturn(mockSensorInfoPreCorrectionActiveArraySize);
+
+      try (MockedStatic<CameraRegionUtils.SizeFactory> mockedSizeFactory =
+          mockStatic(CameraRegionUtils.SizeFactory.class)) {
+        mockedSizeFactory
+            .when(() -> CameraRegionUtils.SizeFactory.create(anyInt(), anyInt()))
+            .thenAnswer(
+                (Answer<Size>)
+                    invocation -> {
+                      Size mockSize = mock(Size.class);
+                      when(mockSize.getWidth()).thenReturn(invocation.getArgument(0));
+                      when(mockSize.getHeight()).thenReturn(invocation.getArgument(1));
+                      return mockSize;
+                    });
+
+        Size result = CameraRegionUtils.getCameraBoundaries(mockCameraProperties, mockBuilder);
+
+        assertEquals(100, result.getWidth());
+        assertEquals(100, result.getHeight());
+        verify(mockCameraProperties, never()).getSensorInfoPixelArraySize();
+        verify(mockCameraProperties, never()).getSensorInfoActiveArraySize();
+      }
     } finally {
       updateSdkVersion(0);
     }
@@ -168,6 +205,9 @@ public void setUp() {
     try {
       CameraProperties mockCameraProperties = mock(CameraProperties.class);
       CaptureRequest.Builder mockBuilder = mock(CaptureRequest.Builder.class);
+      Rect mockSensorInfoActiveArraySize = mock(Rect.class);
+      when(mockSensorInfoActiveArraySize.width()).thenReturn(100);
+      when(mockSensorInfoActiveArraySize.height()).thenReturn(100);
 
       when(mockCameraProperties.getDistortionCorrectionAvailableModes())
           .thenReturn(
@@ -178,13 +218,29 @@ public void setUp() {
 
       when(mockBuilder.get(CaptureRequest.DISTORTION_CORRECTION_MODE))
           .thenReturn(CaptureRequest.DISTORTION_CORRECTION_MODE_FAST);
-      when(mockCameraProperties.getSensorInfoActiveArraySize()).thenReturn(null);
-
-      Size result = CameraRegionUtils.getCameraBoundaries(mockCameraProperties, mockBuilder);
-
-      assertNull(result);
-      verify(mockCameraProperties, never()).getSensorInfoPixelArraySize();
-      verify(mockCameraProperties, never()).getSensorInfoPreCorrectionActiveArraySize();
+      when(mockCameraProperties.getSensorInfoActiveArraySize())
+          .thenReturn(mockSensorInfoActiveArraySize);
+
+      try (MockedStatic<CameraRegionUtils.SizeFactory> mockedSizeFactory =
+          mockStatic(CameraRegionUtils.SizeFactory.class)) {
+        mockedSizeFactory
+            .when(() -> CameraRegionUtils.SizeFactory.create(anyInt(), anyInt()))
+            .thenAnswer(
+                (Answer<Size>)
+                    invocation -> {
+                      Size mockSize = mock(Size.class);
+                      when(mockSize.getWidth()).thenReturn(invocation.getArgument(0));
+                      when(mockSize.getHeight()).thenReturn(invocation.getArgument(1));
+                      return mockSize;
+                    });
+
+        Size result = CameraRegionUtils.getCameraBoundaries(mockCameraProperties, mockBuilder);
+
+        assertEquals(100, result.getWidth());
+        assertEquals(100, result.getHeight());
+        verify(mockCameraProperties, never()).getSensorInfoPixelArraySize();
+        verify(mockCameraProperties, never()).getSensorInfoPreCorrectionActiveArraySize();
+      }
     } finally {
       updateSdkVersion(0);
     }