Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/camera/camera_android/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 0.10.0+1

* Fixes issues with Virtual cameras hiding physical cameras in android 11+.

## 0.10.0

* **Breaking Change** Updates Android camera access permission error codes to be consistent with other platforms. If your app still handles the legacy `cameraPermission` exception, please update it to handle the new permission exception codes that are noted in the README.
Expand Down
1 change: 1 addition & 0 deletions packages/camera/camera_android/android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ android {
}

dependencies {
testImplementation project(path: ':camera_android')
compileOnly 'androidx.annotation:annotation:1.1.0'
testImplementation 'junit:junit:4.13.2'
testImplementation 'org.mockito:mockito-inline:4.6.1'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,13 +147,35 @@ public interface CameraProperties {
* height and crop region height, for @see android.scaler.cropRegion.
*
* <p>By default maps to the @see
* android.hardware.camera2.CameraCharacteristics#SCALER_AVAILABLE_MAX_DIGITAL_ZOOM key.
* 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
* area height and crop region height.
*/
Float getScalerAvailableMaxDigitalZoom();

/**
* Returns the minimum ratio between the default camera zoom setting and all of the available zoom
*
* <p>By default maps to the @see
* android.hardware.camera2.CameraCharacteristics#CONTROL_ZOOM_RATIO_RANGE key's lower value.
*
* @return Float Minimum ratio between the default zoom ratio and the maximum possible zoom
*/
@RequiresApi(api = VERSION_CODES.R)
Float getScalerMinZoomRatio();

/**
* Returns the maximum ratio between the default camera zoom setting and all of the available zoom
*
* <p>By default maps to the @see
* android.hardware.camera2.CameraCharacteristics#CONTROL_ZOOM_RATIO_RANGE key's upper value.
*
* @return Float Maximum ratio between the default zoom ratio and the maximum possible zoom
*/
@RequiresApi(api = VERSION_CODES.R)
Float getScalerMaxZoomRatio();

/**
* Returns the area of the image sensor which corresponds to active pixels after any geometric
* distortion correction has been applied.
Expand Down Expand Up @@ -315,6 +337,18 @@ public Float getScalerAvailableMaxDigitalZoom() {
return cameraCharacteristics.get(CameraCharacteristics.SCALER_AVAILABLE_MAX_DIGITAL_ZOOM);
}

@RequiresApi(api = VERSION_CODES.R)
@Override
public Float getScalerMaxZoomRatio() {
return cameraCharacteristics.get(CameraCharacteristics.CONTROL_ZOOM_RATIO_RANGE).getUpper();
}

@RequiresApi(api = VERSION_CODES.R)
@Override
public Float getScalerMinZoomRatio() {
return cameraCharacteristics.get(CameraCharacteristics.CONTROL_ZOOM_RATIO_RANGE).getLower();
}

@Override
public Rect getSensorInfoActiveArraySize() {
return cameraCharacteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,17 @@

import android.graphics.Rect;
import android.hardware.camera2.CaptureRequest;
import android.os.Build;
import io.flutter.plugins.camera.CameraProperties;
import io.flutter.plugins.camera.features.CameraFeature;

/** Controls the zoom configuration on the {@link android.hardware.camera2} API. */
public class ZoomLevelFeature extends CameraFeature<Float> {
private static final float MINIMUM_ZOOM_LEVEL = 1.0f;
private final boolean hasSupport;
private final Rect sensorArraySize;
private Float currentSetting = MINIMUM_ZOOM_LEVEL;
private Float maximumZoomLevel = MINIMUM_ZOOM_LEVEL;
private Float currentSetting = 1.0f;
private Float minimumZoomLevel = currentSetting;
private Float maximumZoomLevel;

/**
* Creates a new instance of the {@link ZoomLevelFeature}.
Expand All @@ -28,18 +29,24 @@ public ZoomLevelFeature(CameraProperties cameraProperties) {
sensorArraySize = cameraProperties.getSensorInfoActiveArraySize();

if (sensorArraySize == null) {
maximumZoomLevel = MINIMUM_ZOOM_LEVEL;
maximumZoomLevel = minimumZoomLevel;
hasSupport = false;
return;
}

Float maxDigitalZoom = cameraProperties.getScalerAvailableMaxDigitalZoom();
maximumZoomLevel =
((maxDigitalZoom == null) || (maxDigitalZoom < MINIMUM_ZOOM_LEVEL))
? MINIMUM_ZOOM_LEVEL
: maxDigitalZoom;
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.R) {
minimumZoomLevel = cameraProperties.getScalerMinZoomRatio();
maximumZoomLevel = cameraProperties.getScalerMaxZoomRatio();
} else {
minimumZoomLevel = 1.0f;
Float maxDigitalZoom = cameraProperties.getScalerAvailableMaxDigitalZoom();
maximumZoomLevel =
((maxDigitalZoom == null) || (maxDigitalZoom < minimumZoomLevel))
? minimumZoomLevel
: maxDigitalZoom;
}

hasSupport = (Float.compare(maximumZoomLevel, MINIMUM_ZOOM_LEVEL) > 0);
hasSupport = (Float.compare(maximumZoomLevel, minimumZoomLevel) > 0);
}

@Override
Expand Down Expand Up @@ -67,11 +74,16 @@ public void updateBuilder(CaptureRequest.Builder requestBuilder) {
if (!checkIsSupported()) {
return;
}

final Rect computedZoom =
ZoomUtils.computeZoom(
currentSetting, sensorArraySize, MINIMUM_ZOOM_LEVEL, maximumZoomLevel);
requestBuilder.set(CaptureRequest.SCALER_CROP_REGION, computedZoom);
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.R) {
requestBuilder.set(
CaptureRequest.CONTROL_ZOOM_RATIO,
ZoomUtils.computeZoomRatio(currentSetting, minimumZoomLevel, maximumZoomLevel));
} else {
final Rect computedZoom =
ZoomUtils.computeZoomRect(
currentSetting, sensorArraySize, minimumZoomLevel, maximumZoomLevel);
requestBuilder.set(CaptureRequest.SCALER_CROP_REGION, computedZoom);
}
}

/**
Expand All @@ -80,7 +92,7 @@ public void updateBuilder(CaptureRequest.Builder requestBuilder) {
* @return The minimum zoom level.
*/
public float getMinimumZoomLevel() {
return MINIMUM_ZOOM_LEVEL;
return minimumZoomLevel;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ final class ZoomUtils {
* @param maximumZoomLevel The maximim supported zoom level.
* @return An image sensor area based on the supplied zoom settings
*/
static Rect computeZoom(
static Rect computeZoomRect(
float zoom, @NonNull Rect sensorArraySize, float minimumZoomLevel, float maximumZoomLevel) {
final float newZoom = MathUtils.clamp(zoom, minimumZoomLevel, maximumZoomLevel);

Expand All @@ -37,4 +37,8 @@ static Rect computeZoom(

return new Rect(centerX - deltaX, centerY - deltaY, centerX + deltaX, centerY + deltaY);
}

static Float computeZoomRatio(float zoom, float minimumZoomLevel, float maximumZoomLevel) {
return MathUtils.clamp(zoom, minimumZoomLevel, maximumZoomLevel);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,30 @@ public void getScalerAvailableMaxDigitalZoomTest() {
assertEquals(actualDigitalZoom, expectedDigitalZoom);
}

@Test
public void getScalerGetScalerMinZoomRatioTest() {
Range zoomRange = mock(Range.class);
when(mockCharacteristics.get(CameraCharacteristics.CONTROL_ZOOM_RATIO_RANGE))
.thenReturn(zoomRange);

Float minZoom = cameraProperties.getScalerMinZoomRatio();

verify(mockCharacteristics, times(1)).get(CameraCharacteristics.CONTROL_ZOOM_RATIO_RANGE);
assertEquals(zoomRange.getLower(), minZoom);
}

@Test
public void getScalerGetScalerMaxZoomRatioTest() {
Range zoomRange = mock(Range.class);
when(mockCharacteristics.get(CameraCharacteristics.CONTROL_ZOOM_RATIO_RANGE))
.thenReturn(zoomRange);

Float maxZoom = cameraProperties.getScalerMaxZoomRatio();

verify(mockCharacteristics, times(1)).get(CameraCharacteristics.CONTROL_ZOOM_RATIO_RANGE);
assertEquals(zoomRange.getUpper(), maxZoom);
}

@Test
public void getSensorInfoActiveArraySizeTest() {
Rect expectedArraySize = mock(Rect.class);
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public void before() {
mockSensorArray = mock(Rect.class);

mockedStaticCameraZoom
.when(() -> ZoomUtils.computeZoom(anyFloat(), any(), anyFloat(), anyFloat()))
.when(() -> ZoomUtils.computeZoomRect(anyFloat(), any(), anyFloat(), anyFloat()))
.thenReturn(mockZoomArea);
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think we can add a test here that utilizes Robolectric to make sure the proper CaptureRequest is sent when updateBuilder is called?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So Robolectric has some major issues mocking this call

mock(CaptureRequest.Builder.class);

Seems it can only be mocked using our standard test runner - checking the rest of the project nothing mocks this while using mockito.

I did however create a small method to help mock which version of the SDK_INT is running in our code and was able to write a few more tests using this to set our build SDK to R or Q depending on the test.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh gotcha. Okay thanks!

Expand Down
Loading