diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/CameraProperties.java b/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/CameraProperties.java new file mode 100644 index 000000000000..a69ddd0410d4 --- /dev/null +++ b/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/CameraProperties.java @@ -0,0 +1,350 @@ +// 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.graphics.Rect; +import android.hardware.camera2.CameraAccessException; +import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CameraManager; +import android.os.Build.VERSION_CODES; +import android.util.Range; +import android.util.Rational; +import android.util.Size; +import androidx.annotation.RequiresApi; + +/** An interface allowing access to the different characteristics of the device's camera. */ +public interface CameraProperties { + + /** + * Returns the name (or identifier) of the camera device. + * + * @return String The name of the camera device. + */ + String getCameraName(); + + /** + * Returns the list of frame rate ranges for @see android.control.aeTargetFpsRange supported by + * this camera device. + * + *

By default maps to the @see + * android.hardware.camera2.CameraCharacteristics#CONTROL_AE_TARGET_FPS_RANGE key. + * + * @return android.util.Range[] List of frame rate ranges supported by this camera + * device. + */ + Range[] getControlAutoExposureAvailableTargetFpsRanges(); + + /** + * Returns the maximum and minimum exposure compensation values for @see + * android.control.aeExposureCompensation, in counts of @see android.control.aeCompensationStep, + * that are supported by this camera device. + * + *

By default maps to the @see + * android.hardware.camera2.CameraCharacteristics#CONTROL_AE_COMPENSATION_RANGE key. + * + * @return android.util.Range Maximum and minimum exposure compensation supported by this + * camera device. + */ + Range getControlAutoExposureCompensationRange(); + + /** + * Returns the smallest step by which the exposure compensation can be changed. + * + *

By default maps to the @see + * android.hardware.camera2.CameraCharacteristics#CONTROL_AE_COMPENSATION_STEP key. + * + * @return double Smallest step by which the exposure compensation can be changed. + */ + double getControlAutoExposureCompensationStep(); + + /** + * Returns a list of auto-focus modes for @see android.control.afMode that are supported by this + * camera device. + * + *

By default maps to the @see + * android.hardware.camera2.CameraCharacteristics#CONTROL_AF_AVAILABLE_MODES key. + * + * @return int[] List of auto-focus modes supported by this camera device. + */ + int[] getControlAutoFocusAvailableModes(); + + /** + * Returns the maximum number of metering regions that can be used by the auto-exposure routine. + * + *

By default maps to the @see + * android.hardware.camera2.CameraCharacteristics#CONTROL_MAX_REGIONS_AE key. + * + * @return Integer Maximum number of metering regions that can be used by the auto-exposure + * routine. + */ + Integer getControlMaxRegionsAutoExposure(); + + /** + * Returns the maximum number of metering regions that can be used by the auto-focus routine. + * + *

By default maps to the @see + * android.hardware.camera2.CameraCharacteristics#CONTROL_MAX_REGIONS_AF key. + * + * @return Integer Maximum number of metering regions that can be used by the auto-focus routine. + */ + Integer getControlMaxRegionsAutoFocus(); + + /** + * Returns a list of distortion correction modes for @see android.distortionCorrection.mode that + * are supported by this camera device. + * + *

By default maps to the @see + * android.hardware.camera2.CameraCharacteristics#DISTORTION_CORRECTION_AVAILABLE_MODES key. + * + * @return int[] List of distortion correction modes supported by this camera device. + */ + @RequiresApi(api = VERSION_CODES.P) + int[] getDistortionCorrectionAvailableModes(); + + /** + * Returns whether this camera device has a flash unit. + * + *

By default maps to the @see + * android.hardware.camera2.CameraCharacteristics#FLASH_INFO_AVAILABLE key. + * + * @return Boolean Whether this camera device has a flash unit. + */ + Boolean getFlashInfoAvailable(); + + /** + * Returns the direction the camera faces relative to device screen. + * + *

Possible values: + * + *

+ * + * By default maps to the @see android.hardware.camera2.CameraCharacteristics.LENS_FACING key. + * + * @return int Direction the camera faces relative to device screen. + */ + int getLensFacing(); + + /** + * Returns the shortest distance from front most surface of the lens that can be brought into + * sharp focus. + * + *

By default maps to the @see + * android.hardware.camera2.CameraCharacteristics#LENS_INFO_MINIMUM_FOCUS_DISTANCE key. + * + * @return Float Shortest distance from front most surface of the lens that can be brought into + * sharp focus. + */ + Float getLensInfoMinimumFocusDistance(); + + /** + * Returns the maximum ratio between both active area width and crop region width, and active area + * height and crop region height, for @see android.scaler.cropRegion. + * + *

By default maps to the @see + * android.hardware.camera2.CameraCharacteristics#SCALER_AVAILABLE_MAX_DIGITAL_ZOOM key. + * + * @return Float Maximum ratio between both active area width and crop region width, and active + * area height and crop region height + */ + Float getScalerAvailableMaxDigitalZoom(); + + /** + * Returns the area of the image sensor which corresponds to active pixels after any geometric + * distortion correction has been applied. + * + *

By default maps to the @see + * android.hardware.camera2.CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE key. + * + * @return android.graphics.Rect area of the image sensor which corresponds to active pixels after + * any geometric distortion correction has been applied. + */ + Rect getSensorInfoActiveArraySize(); + + /** + * Returns the dimensions of the full pixel array, possibly including black calibration pixels. + * + *

By default maps to the @see + * android.hardware.camera2.CameraCharacteristics#SENSOR_INFO_PIXEL_ARRAY_SIZE key. + * + * @return android.util.Size Dimensions of the full pixel array, possibly including black + * calibration pixels. + */ + Size getSensorInfoPixelArraySize(); + + /** + * Returns the area of the image sensor which corresponds to active pixels prior to the + * application of any geometric distortion correction. + * + *

By default maps to the @see + * android.hardware.camera2.CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE + * key. + * + * @return android.graphics.Rect Area of the image sensor which corresponds to active pixels prior + * to the application of any geometric distortion correction. + */ + @RequiresApi(api = VERSION_CODES.M) + Rect getSensorInfoPreCorrectionActiveArraySize(); + + /** + * Returns the clockwise angle through which the output image needs to be rotated to be upright on + * the device screen in its native orientation. + * + *

By default maps to the @see + * android.hardware.camera2.CameraCharacteristics#SENSOR_ORIENTATION key. + * + * @return int Clockwise angle through which the output image needs to be rotated to be upright on + * the device screen in its native orientation. + */ + int getSensorOrientation(); + + /** + * Returns a level which generally classifies the overall set of the camera device functionality. + * + *

Possible values: + * + *

+ * + * By default maps to the @see + * android.hardware.camera2.CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL key. + * + * @return int Level which generally classifies the overall set of the camera device + * functionality. + */ + int getHardwareLevel(); + + /** + * Returns a list of noise reduction modes for @see android.noiseReduction.mode that are supported + * by this camera device. + * + *

By default maps to the @see + * android.hardware.camera2.CameraCharacteristics#NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES + * key. + * + * @return int[] List of noise reduction modes that are supported by this camera device. + */ + int[] getAvailableNoiseReductionModes(); +} + +/** + * Implementation of the @see CameraProperties interface using the @see + * android.hardware.camera2.CameraCharacteristics class to access the different characteristics. + */ +class CameraPropertiesImpl implements CameraProperties { + private final CameraCharacteristics cameraCharacteristics; + private final String cameraName; + + public CameraPropertiesImpl(String cameraName, CameraManager cameraManager) + throws CameraAccessException { + this.cameraName = cameraName; + this.cameraCharacteristics = cameraManager.getCameraCharacteristics(cameraName); + } + + @Override + public String getCameraName() { + return cameraName; + } + + @Override + public Range[] getControlAutoExposureAvailableTargetFpsRanges() { + return cameraCharacteristics.get(CameraCharacteristics.CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES); + } + + @Override + public Range getControlAutoExposureCompensationRange() { + return cameraCharacteristics.get(CameraCharacteristics.CONTROL_AE_COMPENSATION_RANGE); + } + + @Override + public double getControlAutoExposureCompensationStep() { + Rational rational = + cameraCharacteristics.get(CameraCharacteristics.CONTROL_AE_COMPENSATION_STEP); + + return rational == null ? 0.0 : rational.doubleValue(); + } + + @Override + public int[] getControlAutoFocusAvailableModes() { + return cameraCharacteristics.get(CameraCharacteristics.CONTROL_AF_AVAILABLE_MODES); + } + + @Override + public Integer getControlMaxRegionsAutoExposure() { + return cameraCharacteristics.get(CameraCharacteristics.CONTROL_MAX_REGIONS_AE); + } + + @Override + public Integer getControlMaxRegionsAutoFocus() { + return cameraCharacteristics.get(CameraCharacteristics.CONTROL_MAX_REGIONS_AF); + } + + @RequiresApi(api = VERSION_CODES.P) + @Override + public int[] getDistortionCorrectionAvailableModes() { + return cameraCharacteristics.get(CameraCharacteristics.DISTORTION_CORRECTION_AVAILABLE_MODES); + } + + @Override + public Boolean getFlashInfoAvailable() { + return cameraCharacteristics.get(CameraCharacteristics.FLASH_INFO_AVAILABLE); + } + + @Override + public int getLensFacing() { + return cameraCharacteristics.get(CameraCharacteristics.LENS_FACING); + } + + @Override + public Float getLensInfoMinimumFocusDistance() { + return cameraCharacteristics.get(CameraCharacteristics.LENS_INFO_MINIMUM_FOCUS_DISTANCE); + } + + @Override + public Float getScalerAvailableMaxDigitalZoom() { + return cameraCharacteristics.get(CameraCharacteristics.SCALER_AVAILABLE_MAX_DIGITAL_ZOOM); + } + + @Override + public Rect getSensorInfoActiveArraySize() { + return cameraCharacteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE); + } + + @Override + public Size getSensorInfoPixelArraySize() { + return cameraCharacteristics.get(CameraCharacteristics.SENSOR_INFO_PIXEL_ARRAY_SIZE); + } + + @RequiresApi(api = VERSION_CODES.M) + @Override + public Rect getSensorInfoPreCorrectionActiveArraySize() { + return cameraCharacteristics.get( + CameraCharacteristics.SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE); + } + + @Override + public int getSensorOrientation() { + return cameraCharacteristics.get(CameraCharacteristics.SENSOR_ORIENTATION); + } + + @Override + public int getHardwareLevel() { + return cameraCharacteristics.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL); + } + + @Override + public int[] getAvailableNoiseReductionModes() { + return cameraCharacteristics.get( + CameraCharacteristics.NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES); + } +} diff --git a/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/CameraFeature.java b/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/CameraFeature.java new file mode 100644 index 000000000000..ad800f5e1163 --- /dev/null +++ b/packages/camera/camera/android/src/main/java/io/flutter/plugins/camera/features/CameraFeature.java @@ -0,0 +1,59 @@ +// 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; + +import android.hardware.camera2.CaptureRequest; +import androidx.annotation.NonNull; +import io.flutter.plugins.camera.CameraProperties; + +/** + * An interface describing a feature in the camera. This holds a setting value of type T and must + * implement a means to check if this setting is supported by the current camera properties. It also + * must implement a builder update method which will update a given capture request builder for this + * feature's current setting value. + * + * @param + */ +public abstract class CameraFeature { + protected final CameraProperties cameraProperties; + + protected CameraFeature(@NonNull CameraProperties cameraProperties) { + this.cameraProperties = cameraProperties; + } + + /** Debug name for this feature. */ + public abstract String getDebugName(); + + /** + * Gets the current value of this feature's setting. + * + * @return Current value of this feature's setting. + */ + public abstract T getValue(); + + /** + * Sets a new value for this feature's setting. + * + * @param value New value for this feature's setting. + */ + public abstract void setValue(T value); + + /** + * Returns whether or not this feature is supported. + * + *

When the feature is not supported any {@see #value} is simply ignored by the camera plugin. + * + * @return boolean Whether or not this feature is supported. + */ + public abstract boolean checkIsSupported(); + + /** + * Updates the setting in a provided {@see android.hardware.camera2.CaptureRequest.Builder}. + * + * @param requestBuilder A {@see android.hardware.camera2.CaptureRequest.Builder} instance used to + * configure the settings and outputs needed to capture a single image from the camera device. + */ + public abstract void updateBuilder(CaptureRequest.Builder requestBuilder); +} diff --git a/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/CameraPropertiesImplTest.java b/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/CameraPropertiesImplTest.java new file mode 100644 index 000000000000..2c0381744191 --- /dev/null +++ b/packages/camera/camera/android/src/test/java/io/flutter/plugins/camera/CameraPropertiesImplTest.java @@ -0,0 +1,280 @@ +// 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.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.graphics.Rect; +import android.hardware.camera2.CameraAccessException; +import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CameraManager; +import android.util.Range; +import android.util.Rational; +import android.util.Size; +import org.junit.Before; +import org.junit.Test; + +public class CameraPropertiesImplTest { + private static final String CAMERA_NAME = "test_camera"; + private final CameraCharacteristics mockCharacteristics = mock(CameraCharacteristics.class); + private final CameraManager mockCameraManager = mock(CameraManager.class); + + private CameraPropertiesImpl cameraProperties; + + @Before + public void before() { + try { + when(mockCameraManager.getCameraCharacteristics(CAMERA_NAME)).thenReturn(mockCharacteristics); + cameraProperties = new CameraPropertiesImpl(CAMERA_NAME, mockCameraManager); + } catch (CameraAccessException e) { + fail(); + } + } + + @Test + public void ctor_Should_return_valid_instance() throws CameraAccessException { + verify(mockCameraManager, times(1)).getCameraCharacteristics(CAMERA_NAME); + assertNotNull(cameraProperties); + } + + @Test + @SuppressWarnings("unchecked") + public void getControlAutoExposureAvailableTargetFpsRangesTest() { + Range mockRange = mock(Range.class); + Range[] mockRanges = new Range[] {mockRange}; + when(mockCharacteristics.get(CameraCharacteristics.CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES)) + .thenReturn(mockRanges); + + Range[] actualRanges = + cameraProperties.getControlAutoExposureAvailableTargetFpsRanges(); + + verify(mockCharacteristics, times(1)) + .get(CameraCharacteristics.CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES); + assertArrayEquals(actualRanges, mockRanges); + } + + @Test + @SuppressWarnings("unchecked") + public void getControlAutoExposureCompensationRangeTest() { + Range mockRange = mock(Range.class); + when(mockCharacteristics.get(CameraCharacteristics.CONTROL_AE_COMPENSATION_RANGE)) + .thenReturn(mockRange); + + Range actualRange = cameraProperties.getControlAutoExposureCompensationRange(); + + verify(mockCharacteristics, times(1)).get(CameraCharacteristics.CONTROL_AE_COMPENSATION_RANGE); + assertEquals(actualRange, mockRange); + } + + @Test + public void + getControlAutoExposureCompensationStep_Should_return_double_When_rational_is_not_null() { + double expectedStep = 3.1415926535; + Rational mockRational = mock(Rational.class); + + when(mockCharacteristics.get(CameraCharacteristics.CONTROL_AE_COMPENSATION_STEP)) + .thenReturn(mockRational); + when(mockRational.doubleValue()).thenReturn(expectedStep); + + double actualSteps = cameraProperties.getControlAutoExposureCompensationStep(); + + verify(mockCharacteristics, times(1)).get(CameraCharacteristics.CONTROL_AE_COMPENSATION_STEP); + assertEquals(actualSteps, expectedStep, 0); + } + + @Test + public void getControlAutoExposureCompensationStep_Should_return_zero_When_rational_is_null() { + double expectedStep = 0.0; + + when(mockCharacteristics.get(CameraCharacteristics.CONTROL_AE_COMPENSATION_STEP)) + .thenReturn(null); + + double actualSteps = cameraProperties.getControlAutoExposureCompensationStep(); + + verify(mockCharacteristics, times(1)).get(CameraCharacteristics.CONTROL_AE_COMPENSATION_STEP); + assertEquals(actualSteps, expectedStep, 0); + } + + @Test + public void getControlAutoFocusAvailableModesTest() { + int[] expectedAutoFocusModes = new int[] {0, 1, 2}; + when(mockCharacteristics.get(CameraCharacteristics.CONTROL_AF_AVAILABLE_MODES)) + .thenReturn(expectedAutoFocusModes); + + int[] actualAutoFocusModes = cameraProperties.getControlAutoFocusAvailableModes(); + + verify(mockCharacteristics, times(1)).get(CameraCharacteristics.CONTROL_AF_AVAILABLE_MODES); + assertEquals(actualAutoFocusModes, expectedAutoFocusModes); + } + + @Test + public void getControlMaxRegionsAutoExposureTest() { + int expectedRegions = 42; + when(mockCharacteristics.get(CameraCharacteristics.CONTROL_MAX_REGIONS_AE)) + .thenReturn(expectedRegions); + + int actualRegions = cameraProperties.getControlMaxRegionsAutoExposure(); + + verify(mockCharacteristics, times(1)).get(CameraCharacteristics.CONTROL_MAX_REGIONS_AE); + assertEquals(actualRegions, expectedRegions); + } + + @Test + public void getControlMaxRegionsAutoFocusTest() { + int expectedRegions = 42; + when(mockCharacteristics.get(CameraCharacteristics.CONTROL_MAX_REGIONS_AF)) + .thenReturn(expectedRegions); + + int actualRegions = cameraProperties.getControlMaxRegionsAutoFocus(); + + verify(mockCharacteristics, times(1)).get(CameraCharacteristics.CONTROL_MAX_REGIONS_AF); + assertEquals(actualRegions, expectedRegions); + } + + @Test + public void getDistortionCorrectionAvailableModesTest() { + int[] expectedCorrectionModes = new int[] {0, 1, 2}; + when(mockCharacteristics.get(CameraCharacteristics.DISTORTION_CORRECTION_AVAILABLE_MODES)) + .thenReturn(expectedCorrectionModes); + + int[] actualCorrectionModes = cameraProperties.getDistortionCorrectionAvailableModes(); + + verify(mockCharacteristics, times(1)) + .get(CameraCharacteristics.DISTORTION_CORRECTION_AVAILABLE_MODES); + assertEquals(actualCorrectionModes, expectedCorrectionModes); + } + + @Test + public void getFlashInfoAvailableTest() { + boolean expectedAvailability = true; + when(mockCharacteristics.get(CameraCharacteristics.FLASH_INFO_AVAILABLE)) + .thenReturn(expectedAvailability); + + boolean actualAvailability = cameraProperties.getFlashInfoAvailable(); + + verify(mockCharacteristics, times(1)).get(CameraCharacteristics.FLASH_INFO_AVAILABLE); + assertEquals(actualAvailability, expectedAvailability); + } + + @Test + public void getLensFacingTest() { + int expectedFacing = 42; + when(mockCharacteristics.get(CameraCharacteristics.LENS_FACING)).thenReturn(expectedFacing); + + int actualFacing = cameraProperties.getLensFacing(); + + verify(mockCharacteristics, times(1)).get(CameraCharacteristics.LENS_FACING); + assertEquals(actualFacing, expectedFacing); + } + + @Test + public void getLensInfoMinimumFocusDistanceTest() { + Float expectedFocusDistance = new Float(3.14); + when(mockCharacteristics.get(CameraCharacteristics.LENS_INFO_MINIMUM_FOCUS_DISTANCE)) + .thenReturn(expectedFocusDistance); + + Float actualFocusDistance = cameraProperties.getLensInfoMinimumFocusDistance(); + + verify(mockCharacteristics, times(1)) + .get(CameraCharacteristics.LENS_INFO_MINIMUM_FOCUS_DISTANCE); + assertEquals(actualFocusDistance, expectedFocusDistance); + } + + @Test + public void getScalerAvailableMaxDigitalZoomTest() { + Float expectedDigitalZoom = new Float(3.14); + when(mockCharacteristics.get(CameraCharacteristics.SCALER_AVAILABLE_MAX_DIGITAL_ZOOM)) + .thenReturn(expectedDigitalZoom); + + Float actualDigitalZoom = cameraProperties.getScalerAvailableMaxDigitalZoom(); + + verify(mockCharacteristics, times(1)) + .get(CameraCharacteristics.SCALER_AVAILABLE_MAX_DIGITAL_ZOOM); + assertEquals(actualDigitalZoom, expectedDigitalZoom); + } + + @Test + public void getSensorInfoActiveArraySizeTest() { + Rect expectedArraySize = mock(Rect.class); + when(mockCharacteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE)) + .thenReturn(expectedArraySize); + + Rect actualArraySize = cameraProperties.getSensorInfoActiveArraySize(); + + verify(mockCharacteristics, times(1)).get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE); + assertEquals(actualArraySize, expectedArraySize); + } + + @Test + public void getSensorInfoPixelArraySizeTest() { + Size expectedArraySize = mock(Size.class); + when(mockCharacteristics.get(CameraCharacteristics.SENSOR_INFO_PIXEL_ARRAY_SIZE)) + .thenReturn(expectedArraySize); + + Size actualArraySize = cameraProperties.getSensorInfoPixelArraySize(); + + verify(mockCharacteristics, times(1)).get(CameraCharacteristics.SENSOR_INFO_PIXEL_ARRAY_SIZE); + assertEquals(actualArraySize, expectedArraySize); + } + + @Test + public void getSensorInfoPreCorrectionActiveArraySize() { + Rect expectedArraySize = mock(Rect.class); + when(mockCharacteristics.get( + CameraCharacteristics.SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE)) + .thenReturn(expectedArraySize); + + Rect actualArraySize = cameraProperties.getSensorInfoPreCorrectionActiveArraySize(); + + verify(mockCharacteristics, times(1)) + .get(CameraCharacteristics.SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE); + assertEquals(actualArraySize, expectedArraySize); + } + + @Test + public void getSensorOrientationTest() { + int expectedOrientation = 42; + when(mockCharacteristics.get(CameraCharacteristics.SENSOR_ORIENTATION)) + .thenReturn(expectedOrientation); + + int actualOrientation = cameraProperties.getSensorOrientation(); + + verify(mockCharacteristics, times(1)).get(CameraCharacteristics.SENSOR_ORIENTATION); + assertEquals(actualOrientation, expectedOrientation); + } + + @Test + public void getHardwareLevelTest() { + int expectedLevel = 42; + when(mockCharacteristics.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL)) + .thenReturn(expectedLevel); + + int actualLevel = cameraProperties.getHardwareLevel(); + + verify(mockCharacteristics, times(1)).get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL); + assertEquals(actualLevel, expectedLevel); + } + + @Test + public void getAvailableNoiseReductionModesTest() { + int[] expectedReductionModes = new int[] {0, 1, 2}; + when(mockCharacteristics.get( + CameraCharacteristics.NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES)) + .thenReturn(expectedReductionModes); + + int[] actualReductionModes = cameraProperties.getAvailableNoiseReductionModes(); + + verify(mockCharacteristics, times(1)) + .get(CameraCharacteristics.NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES); + assertEquals(actualReductionModes, expectedReductionModes); + } +}