blob: 80828929a132bcf45d5fef679a781e73cdcdd042 [file] [log] [blame]
/*
* Copyright 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.camera.camera2.impl;
import static android.hardware.camera2.CameraMetadata.CONTROL_AE_MODE_OFF;
import static android.hardware.camera2.CameraMetadata.CONTROL_AE_MODE_ON;
import static android.hardware.camera2.CameraMetadata.CONTROL_AE_MODE_ON_ALWAYS_FLASH;
import static android.hardware.camera2.CameraMetadata.CONTROL_AE_MODE_ON_AUTO_FLASH;
import static android.hardware.camera2.CameraMetadata.CONTROL_AF_MODE_AUTO;
import static android.hardware.camera2.CameraMetadata.CONTROL_AF_MODE_CONTINUOUS_PICTURE;
import static android.hardware.camera2.CameraMetadata.CONTROL_AF_MODE_OFF;
import static android.hardware.camera2.CameraMetadata.CONTROL_AWB_MODE_AUTO;
import static android.hardware.camera2.CameraMetadata.CONTROL_AWB_MODE_OFF;
import static android.hardware.camera2.CameraMetadata.FLASH_MODE_OFF;
import static android.hardware.camera2.CameraMetadata.FLASH_MODE_TORCH;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.junit.Assume.assumeTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.content.Context;
import android.graphics.Rect;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CaptureRequest;
import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
import androidx.annotation.NonNull;
import androidx.camera.camera2.Camera2Config;
import androidx.camera.core.CameraControl;
import androidx.camera.core.CameraControlInternal;
import androidx.camera.core.CameraInfoUnavailableException;
import androidx.camera.core.CaptureConfig;
import androidx.camera.core.FlashMode;
import androidx.camera.core.FocusMeteringAction;
import androidx.camera.core.FocusMeteringAction.MeteringMode;
import androidx.camera.core.LensFacing;
import androidx.camera.core.SensorOrientedMeteringPointFactory;
import androidx.camera.core.SessionConfig;
import androidx.camera.core.impl.utils.executor.CameraXExecutors;
import androidx.camera.testing.CameraUtil;
import androidx.camera.testing.HandlerUtil;
import androidx.core.os.HandlerCompat;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.google.common.util.concurrent.ListenableFuture;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
@SmallTest
@RunWith(AndroidJUnit4.class)
public final class Camera2CameraControlTest {
private Camera2CameraControl mCamera2CameraControl;
private CameraControlInternal.ControlUpdateCallback mControlUpdateCallback;
private ArgumentCaptor<SessionConfig> mSessionConfigArgumentCaptor =
ArgumentCaptor.forClass(SessionConfig.class);
@SuppressWarnings("unchecked")
private ArgumentCaptor<List<CaptureConfig>> mCaptureConfigArgumentCaptor =
ArgumentCaptor.forClass(List.class);
private HandlerThread mHandlerThread;
private Handler mHandler;
private CameraCharacteristics mCameraCharacteristics;
@Before
public void setUp() throws InterruptedException, CameraAccessException,
CameraInfoUnavailableException {
assumeTrue(CameraUtil.hasCameraWithLensFacing(LensFacing.BACK));
Context context = ApplicationProvider.getApplicationContext();
CameraManager cameraManager = (CameraManager) context.getSystemService(
Context.CAMERA_SERVICE);
Camera2CameraFactory camera2CameraFactory = new Camera2CameraFactory(context);
mCameraCharacteristics = cameraManager.getCameraCharacteristics(
camera2CameraFactory.cameraIdForLensFacing(LensFacing.BACK));
mControlUpdateCallback = mock(CameraControlInternal.ControlUpdateCallback.class);
mHandlerThread = new HandlerThread("ControlThread");
mHandlerThread.start();
mHandler = HandlerCompat.createAsync(mHandlerThread.getLooper());
ScheduledExecutorService executorService = CameraXExecutors.newHandlerExecutor(mHandler);
mCamera2CameraControl = new Camera2CameraControl(mCameraCharacteristics,
executorService, executorService, mControlUpdateCallback);
mCamera2CameraControl.setActive(true);
HandlerUtil.waitForLooperToIdle(mHandler);
// Reset the method call onCameraControlUpdateSessionConfig() in Camera2CameraControl
// constructor.
reset(mControlUpdateCallback);
}
@After
public void tearDown() {
if (mHandlerThread != null) {
mHandlerThread.quitSafely();
}
}
@Test
public void setCropRegion_cropRectSetAndRepeatingRequestUpdated() throws InterruptedException {
Rect rect = new Rect(0, 0, 10, 10);
mCamera2CameraControl.setCropRegion(rect);
HandlerUtil.waitForLooperToIdle(mHandler);
verify(mControlUpdateCallback, times(1)).onCameraControlUpdateSessionConfig(
mSessionConfigArgumentCaptor.capture());
SessionConfig sessionConfig = mSessionConfigArgumentCaptor.getValue();
Camera2Config repeatingConfig = new Camera2Config(sessionConfig.getImplementationOptions());
assertThat(repeatingConfig.getCaptureRequestOption(CaptureRequest.SCALER_CROP_REGION, null))
.isEqualTo(rect);
Camera2Config singleConfig = new Camera2Config(mCamera2CameraControl.getSessionOptions());
assertThat(singleConfig.getCaptureRequestOption(CaptureRequest.SCALER_CROP_REGION, null))
.isEqualTo(rect);
}
@Test
public void defaultAFAWBMode_ShouldBeCAFWhenNotFocusLocked() {
Camera2Config singleConfig = new Camera2Config(mCamera2CameraControl.getSessionOptions());
assertThat(
singleConfig.getCaptureRequestOption(
CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_OFF))
.isEqualTo(CaptureRequest.CONTROL_MODE_AUTO);
assertAfMode(singleConfig, CONTROL_AF_MODE_CONTINUOUS_PICTURE);
assertAwbMode(singleConfig, CONTROL_AWB_MODE_AUTO);
}
@Test
public void setFlashModeAuto_aeModeSetAndRequestUpdated() throws InterruptedException {
mCamera2CameraControl.setFlashMode(FlashMode.AUTO);
HandlerUtil.waitForLooperToIdle(mHandler);
verify(mControlUpdateCallback, times(1)).onCameraControlUpdateSessionConfig(
mSessionConfigArgumentCaptor.capture());
SessionConfig sessionConfig = mSessionConfigArgumentCaptor.getValue();
Camera2Config camera2Config = new Camera2Config(sessionConfig.getImplementationOptions());
assertAeMode(camera2Config, CONTROL_AE_MODE_ON_AUTO_FLASH);
assertThat(mCamera2CameraControl.getFlashMode()).isEqualTo(FlashMode.AUTO);
}
@Test
public void setFlashModeOff_aeModeSetAndRequestUpdated() throws InterruptedException {
mCamera2CameraControl.setFlashMode(FlashMode.OFF);
HandlerUtil.waitForLooperToIdle(mHandler);
verify(mControlUpdateCallback, times(1)).onCameraControlUpdateSessionConfig(
mSessionConfigArgumentCaptor.capture());
SessionConfig sessionConfig = mSessionConfigArgumentCaptor.getValue();
Camera2Config camera2Config = new Camera2Config(sessionConfig.getImplementationOptions());
assertAeMode(camera2Config, CONTROL_AE_MODE_ON);
assertThat(mCamera2CameraControl.getFlashMode()).isEqualTo(FlashMode.OFF);
}
@Test
public void setFlashModeOn_aeModeSetAndRequestUpdated() throws InterruptedException {
mCamera2CameraControl.setFlashMode(FlashMode.ON);
HandlerUtil.waitForLooperToIdle(mHandler);
verify(mControlUpdateCallback, times(1)).onCameraControlUpdateSessionConfig(
mSessionConfigArgumentCaptor.capture());
SessionConfig sessionConfig = mSessionConfigArgumentCaptor.getValue();
Camera2Config camera2Config = new Camera2Config(sessionConfig.getImplementationOptions());
assertAeMode(camera2Config, CONTROL_AE_MODE_ON_ALWAYS_FLASH);
assertThat(mCamera2CameraControl.getFlashMode()).isEqualTo(FlashMode.ON);
}
@Test
public void enableTorch_aeModeSetAndRequestUpdated() throws InterruptedException {
mCamera2CameraControl.enableTorch(true);
HandlerUtil.waitForLooperToIdle(mHandler);
verify(mControlUpdateCallback, times(1)).onCameraControlUpdateSessionConfig(
mSessionConfigArgumentCaptor.capture());
SessionConfig sessionConfig = mSessionConfigArgumentCaptor.getValue();
Camera2Config camera2Config = new Camera2Config(sessionConfig.getImplementationOptions());
assertAeMode(camera2Config, CONTROL_AE_MODE_ON);
assertThat(
camera2Config.getCaptureRequestOption(
CaptureRequest.FLASH_MODE, FLASH_MODE_OFF))
.isEqualTo(FLASH_MODE_TORCH);
assertThat(mCamera2CameraControl.isTorchOn()).isTrue();
}
@Test
public void disableTorchFlashModeAuto_aeModeSetAndRequestUpdated() throws InterruptedException {
mCamera2CameraControl.setFlashMode(FlashMode.AUTO);
mCamera2CameraControl.enableTorch(false);
HandlerUtil.waitForLooperToIdle(mHandler);
verify(mControlUpdateCallback, times(2)).onCameraControlUpdateSessionConfig(
mSessionConfigArgumentCaptor.capture());
SessionConfig sessionConfig = mSessionConfigArgumentCaptor.getAllValues().get(0);
Camera2Config camera2Config = new Camera2Config(sessionConfig.getImplementationOptions());
assertAeMode(camera2Config, CONTROL_AE_MODE_ON_AUTO_FLASH);
assertThat(camera2Config.getCaptureRequestOption(CaptureRequest.FLASH_MODE, -1))
.isEqualTo(-1);
assertThat(mCamera2CameraControl.isTorchOn()).isFalse();
verify(mControlUpdateCallback, times(1)).onCameraControlCaptureRequests(
mCaptureConfigArgumentCaptor.capture());
CaptureConfig captureConfig = mCaptureConfigArgumentCaptor.getValue().get(0);
Camera2Config resultCaptureConfig =
new Camera2Config(captureConfig.getImplementationOptions());
assertAeMode(resultCaptureConfig, CONTROL_AE_MODE_ON);
}
@Test
public void triggerAf_captureRequestSent() throws InterruptedException {
mCamera2CameraControl.triggerAf();
HandlerUtil.waitForLooperToIdle(mHandler);
verify(mControlUpdateCallback).onCameraControlCaptureRequests(
mCaptureConfigArgumentCaptor.capture());
CaptureConfig captureConfig = mCaptureConfigArgumentCaptor.getValue().get(0);
Camera2Config resultCaptureConfig =
new Camera2Config(captureConfig.getImplementationOptions());
assertThat(
resultCaptureConfig.getCaptureRequestOption(
CaptureRequest.CONTROL_AF_TRIGGER, null))
.isEqualTo(CaptureRequest.CONTROL_AF_TRIGGER_START);
}
@Test
public void triggerAePrecapture_captureRequestSent() throws InterruptedException {
mCamera2CameraControl.triggerAePrecapture();
HandlerUtil.waitForLooperToIdle(mHandler);
verify(mControlUpdateCallback).onCameraControlCaptureRequests(
mCaptureConfigArgumentCaptor.capture());
CaptureConfig captureConfig = mCaptureConfigArgumentCaptor.getValue().get(0);
Camera2Config resultCaptureConfig =
new Camera2Config(captureConfig.getImplementationOptions());
assertThat(
resultCaptureConfig.getCaptureRequestOption(
CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER, null))
.isEqualTo(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START);
}
@Test
public void cancelAfAeTrigger_captureRequestSent() throws InterruptedException {
mCamera2CameraControl.cancelAfAeTrigger(true, true);
HandlerUtil.waitForLooperToIdle(mHandler);
verify(mControlUpdateCallback).onCameraControlCaptureRequests(
mCaptureConfigArgumentCaptor.capture());
CaptureConfig captureConfig = mCaptureConfigArgumentCaptor.getValue().get(0);
Camera2Config resultCaptureConfig =
new Camera2Config(captureConfig.getImplementationOptions());
assertThat(
resultCaptureConfig.getCaptureRequestOption(
CaptureRequest.CONTROL_AF_TRIGGER, null))
.isEqualTo(CaptureRequest.CONTROL_AF_TRIGGER_CANCEL);
if (Build.VERSION.SDK_INT >= 23) {
assertThat(
resultCaptureConfig.getCaptureRequestOption(
CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER, null))
.isEqualTo(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_CANCEL);
}
}
@Test
public void cancelAfTrigger_captureRequestSent() throws InterruptedException {
mCamera2CameraControl.cancelAfAeTrigger(true, false);
HandlerUtil.waitForLooperToIdle(mHandler);
verify(mControlUpdateCallback).onCameraControlCaptureRequests(
mCaptureConfigArgumentCaptor.capture());
CaptureConfig captureConfig = mCaptureConfigArgumentCaptor.getValue().get(0);
Camera2Config resultCaptureConfig =
new Camera2Config(captureConfig.getImplementationOptions());
assertThat(
resultCaptureConfig.getCaptureRequestOption(
CaptureRequest.CONTROL_AF_TRIGGER, null))
.isEqualTo(CaptureRequest.CONTROL_AF_TRIGGER_CANCEL);
assertThat(
resultCaptureConfig.getCaptureRequestOption(
CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER, null))
.isNull();
}
@Test
public void cancelAeTrigger_captureRequestSent() throws InterruptedException {
mCamera2CameraControl.cancelAfAeTrigger(false, true);
HandlerUtil.waitForLooperToIdle(mHandler);
verify(mControlUpdateCallback).onCameraControlCaptureRequests(
mCaptureConfigArgumentCaptor.capture());
CaptureConfig captureConfig = mCaptureConfigArgumentCaptor.getValue().get(0);
Camera2Config resultCaptureConfig =
new Camera2Config(captureConfig.getImplementationOptions());
assertThat(
resultCaptureConfig.getCaptureRequestOption(
CaptureRequest.CONTROL_AF_TRIGGER, null))
.isNull();
if (Build.VERSION.SDK_INT >= 23) {
assertThat(
resultCaptureConfig.getCaptureRequestOption(
CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER, null))
.isEqualTo(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_CANCEL);
}
}
@Test
public void startFocusAndMetering_3ARegionsUpdatedInSessionAndSessionOptions()
throws InterruptedException {
SensorOrientedMeteringPointFactory factory = new SensorOrientedMeteringPointFactory(1.0f,
1.0f);
FocusMeteringAction action = FocusMeteringAction.Builder.from(factory.createPoint(0, 0))
.build();
mCamera2CameraControl.startFocusAndMetering(action);
HandlerUtil.waitForLooperToIdle(mHandler);
verify(mControlUpdateCallback, times(1)).onCameraControlUpdateSessionConfig(
mSessionConfigArgumentCaptor.capture());
SessionConfig sessionConfig = mSessionConfigArgumentCaptor.getValue();
Camera2Config repeatingConfig = new Camera2Config(sessionConfig.getImplementationOptions());
// Here we verify only 3A region count is correct. Values correctness are left to
// FocusMeteringControlTest.
assertThat(
repeatingConfig.getCaptureRequestOption(
CaptureRequest.CONTROL_AF_REGIONS, null)).hasLength(1);
assertThat(
repeatingConfig.getCaptureRequestOption(
CaptureRequest.CONTROL_AE_REGIONS, null)).hasLength(1);
assertThat(
repeatingConfig.getCaptureRequestOption(
CaptureRequest.CONTROL_AWB_REGIONS, null)).hasLength(1);
Camera2Config singleConfig = new Camera2Config(mCamera2CameraControl.getSessionOptions());
assertThat(
singleConfig.getCaptureRequestOption(
CaptureRequest.CONTROL_AF_REGIONS, null)).hasLength(1);
assertThat(
singleConfig.getCaptureRequestOption(
CaptureRequest.CONTROL_AE_REGIONS, null)).hasLength(1);
assertThat(
singleConfig.getCaptureRequestOption(
CaptureRequest.CONTROL_AWB_REGIONS, null)).hasLength(1);
}
@Test
public void startFocusAndMetering_AfIsTriggeredProperly() throws InterruptedException {
SensorOrientedMeteringPointFactory factory = new SensorOrientedMeteringPointFactory(1.0f,
1.0f);
FocusMeteringAction action = FocusMeteringAction.Builder.from(factory.createPoint(0, 0))
.build();
mCamera2CameraControl.startFocusAndMetering(action);
HandlerUtil.waitForLooperToIdle(mHandler);
verifyAfMode(CaptureRequest.CONTROL_AF_MODE_AUTO);
verify(mControlUpdateCallback).onCameraControlCaptureRequests(
mCaptureConfigArgumentCaptor.capture());
CaptureConfig captureConfig = mCaptureConfigArgumentCaptor.getValue().get(0);
Camera2Config resultCaptureConfig =
new Camera2Config(captureConfig.getImplementationOptions());
// Trigger AF
assertThat(resultCaptureConfig.getCaptureRequestOption(CaptureRequest.CONTROL_AF_TRIGGER,
null)).isEqualTo(CaptureRequest.CONTROL_AF_TRIGGER_START);
}
@Test
public void startFocusAndMetering_AFNotInvolved_AfIsNotTriggered() throws InterruptedException {
SensorOrientedMeteringPointFactory factory = new SensorOrientedMeteringPointFactory(1.0f,
1.0f);
FocusMeteringAction action = FocusMeteringAction.Builder.from(factory.createPoint(0, 0),
MeteringMode.AE_AWB)
.build();
mCamera2CameraControl.startFocusAndMetering(action);
HandlerUtil.waitForLooperToIdle(mHandler);
verifyAfMode(CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
verify(mControlUpdateCallback, never()).onCameraControlCaptureRequests(any());
}
@Test
public void cancelFocusAndMetering_3ARegionsReset() throws InterruptedException {
SensorOrientedMeteringPointFactory factory = new SensorOrientedMeteringPointFactory(1.0f,
1.0f);
FocusMeteringAction action = FocusMeteringAction.Builder.from(factory.createPoint(0, 0))
.build();
mCamera2CameraControl.startFocusAndMetering(action);
HandlerUtil.waitForLooperToIdle(mHandler);
Mockito.reset(mControlUpdateCallback);
mCamera2CameraControl.cancelFocusAndMetering();
HandlerUtil.waitForLooperToIdle(mHandler);
verify(mControlUpdateCallback, times(1)).onCameraControlUpdateSessionConfig(
mSessionConfigArgumentCaptor.capture());
SessionConfig sessionConfig = mSessionConfigArgumentCaptor.getValue();
Camera2Config repeatingConfig = new Camera2Config(sessionConfig.getImplementationOptions());
assertThat(
repeatingConfig.getCaptureRequestOption(
CaptureRequest.CONTROL_AF_REGIONS, null)).isNull();
assertThat(
repeatingConfig.getCaptureRequestOption(
CaptureRequest.CONTROL_AE_REGIONS, null)).isNull();
assertThat(
repeatingConfig.getCaptureRequestOption(
CaptureRequest.CONTROL_AWB_REGIONS, null)).isNull();
Camera2Config singleConfig = new Camera2Config(mCamera2CameraControl.getSessionOptions());
assertThat(
singleConfig.getCaptureRequestOption(
CaptureRequest.CONTROL_AF_REGIONS, null)).isNull();
assertThat(
singleConfig.getCaptureRequestOption(
CaptureRequest.CONTROL_AE_REGIONS, null)).isNull();
assertThat(
singleConfig.getCaptureRequestOption(
CaptureRequest.CONTROL_AWB_REGIONS, null)).isNull();
}
@Test
public void cancelFocusAndMetering_cancelAfProperly() throws InterruptedException {
SensorOrientedMeteringPointFactory factory = new SensorOrientedMeteringPointFactory(1.0f,
1.0f);
FocusMeteringAction action = FocusMeteringAction.Builder.from(factory.createPoint(0, 0))
.build();
mCamera2CameraControl.startFocusAndMetering(action);
HandlerUtil.waitForLooperToIdle(mHandler);
Mockito.reset(mControlUpdateCallback);
mCamera2CameraControl.cancelFocusAndMetering();
HandlerUtil.waitForLooperToIdle(mHandler);
verifyAfMode(CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
verify(mControlUpdateCallback).onCameraControlCaptureRequests(
mCaptureConfigArgumentCaptor.capture());
CaptureConfig captureConfig = mCaptureConfigArgumentCaptor.getValue().get(0);
Camera2Config resultCaptureConfig =
new Camera2Config(captureConfig.getImplementationOptions());
// Trigger AF
assertThat(resultCaptureConfig.getCaptureRequestOption(CaptureRequest.CONTROL_AF_TRIGGER,
null)).isEqualTo(CaptureRequest.CONTROL_AF_TRIGGER_CANCEL);
}
private void verifyAfMode(int expectAfMode) {
verify(mControlUpdateCallback, times(1)).onCameraControlUpdateSessionConfig(
mSessionConfigArgumentCaptor.capture());
SessionConfig sessionConfig = mSessionConfigArgumentCaptor.getValue();
Camera2Config repeatingConfig = new Camera2Config(sessionConfig.getImplementationOptions());
assertAfMode(repeatingConfig, expectAfMode);
}
@Test
public void cancelFocusAndMetering_AFNotInvolved_notCancelAF() throws InterruptedException {
SensorOrientedMeteringPointFactory factory = new SensorOrientedMeteringPointFactory(1.0f,
1.0f);
FocusMeteringAction action = FocusMeteringAction.Builder.from(factory.createPoint(0, 0),
MeteringMode.AE_ONLY)
.build();
mCamera2CameraControl.startFocusAndMetering(action);
HandlerUtil.waitForLooperToIdle(mHandler);
Mockito.reset(mControlUpdateCallback);
mCamera2CameraControl.cancelFocusAndMetering();
HandlerUtil.waitForLooperToIdle(mHandler);
verify(mControlUpdateCallback, never()).onCameraControlCaptureRequests(any());
verifyAfMode(CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
}
@Test
public void startFocus_afModeIsSetToAuto() throws InterruptedException {
SensorOrientedMeteringPointFactory factory = new SensorOrientedMeteringPointFactory(1.0f,
1.0f);
FocusMeteringAction action = FocusMeteringAction.Builder.from(factory.createPoint(0, 0))
.build();
mCamera2CameraControl.startFocusAndMetering(action);
HandlerUtil.waitForLooperToIdle(mHandler);
Camera2Config singleConfig = new Camera2Config(mCamera2CameraControl.getSessionOptions());
assertAfMode(singleConfig, CaptureRequest.CONTROL_AF_MODE_AUTO);
mCamera2CameraControl.cancelFocusAndMetering();
HandlerUtil.waitForLooperToIdle(mHandler);
Camera2Config singleConfig2 = new Camera2Config(mCamera2CameraControl.getSessionOptions());
assertAfMode(singleConfig2, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
}
private boolean isAfModeSupported(int afMode) {
int[] modes = mCameraCharacteristics.get(CameraCharacteristics.CONTROL_AF_AVAILABLE_MODES);
return isModeInList(afMode, modes);
}
private boolean isAeModeSupported(int aeMode) {
int[] modes = mCameraCharacteristics.get(CameraCharacteristics.CONTROL_AE_AVAILABLE_MODES);
return isModeInList(aeMode, modes);
}
private boolean isAwbModeSupported(int awbMode) {
int[] modes = mCameraCharacteristics.get(CameraCharacteristics.CONTROL_AWB_AVAILABLE_MODES);
return isModeInList(awbMode, modes);
}
private boolean isModeInList(int mode, int[] modeList) {
if (modeList == null) {
return false;
}
for (int m : modeList) {
if (mode == m) {
return true;
}
}
return false;
}
private void assertAfMode(Camera2Config config, int afMode) {
if (isAfModeSupported(afMode)) {
assertThat(config.getCaptureRequestOption(
CaptureRequest.CONTROL_AF_MODE, null)).isEqualTo(afMode);
} else {
int fallbackMode;
if (isAfModeSupported(CONTROL_AF_MODE_CONTINUOUS_PICTURE)) {
fallbackMode = CONTROL_AF_MODE_CONTINUOUS_PICTURE;
} else if (isAfModeSupported(CONTROL_AF_MODE_AUTO)) {
fallbackMode = CONTROL_AF_MODE_AUTO;
} else {
fallbackMode = CONTROL_AF_MODE_OFF;
}
assertThat(config.getCaptureRequestOption(
CaptureRequest.CONTROL_AF_MODE, null)).isEqualTo(fallbackMode);
}
}
private void assertAeMode(Camera2Config config, int aeMode) {
if (isAeModeSupported(aeMode)) {
assertThat(config.getCaptureRequestOption(
CaptureRequest.CONTROL_AE_MODE, null)).isEqualTo(aeMode);
} else {
int fallbackMode;
if (isAeModeSupported(CONTROL_AE_MODE_ON)) {
fallbackMode = CONTROL_AE_MODE_ON;
} else {
fallbackMode = CONTROL_AE_MODE_OFF;
}
assertThat(config.getCaptureRequestOption(
CaptureRequest.CONTROL_AE_MODE, null)).isEqualTo(fallbackMode);
}
}
private void assertAwbMode(Camera2Config config, int awbMode) {
if (isAwbModeSupported(awbMode)) {
assertThat(config.getCaptureRequestOption(
CaptureRequest.CONTROL_AWB_MODE, null)).isEqualTo(awbMode);
} else {
int fallbackMode;
if (isAwbModeSupported(CONTROL_AWB_MODE_AUTO)) {
fallbackMode = CONTROL_AWB_MODE_AUTO;
} else {
fallbackMode = CONTROL_AWB_MODE_OFF;
}
assertThat(config.getCaptureRequestOption(
CaptureRequest.CONTROL_AWB_MODE, null)).isEqualTo(fallbackMode);
}
}
private boolean isZoomSupported() {
return mCameraCharacteristics.get(CameraCharacteristics.SCALER_AVAILABLE_MAX_DIGITAL_ZOOM)
> 1.0f;
}
private Rect getSensorRect() {
Rect rect = mCameraCharacteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
// Some device like pixel 2 will have (0, 8) as the left-top corner.
return new Rect(0, 0, rect.width(), rect.height());
}
// Here we just test if setZoomRatio / setZoomPercentage is working. For thorough tests, we
// do it on ZoomControlTest and ZoomControlRoboTest.
@Test
public void setZoomRatio_CropRegionIsUpdatedCorrectly() throws InterruptedException {
assumeTrue(isZoomSupported());
mCamera2CameraControl.setZoomRatio(2.0f);
HandlerUtil.waitForLooperToIdle(mHandler);
Rect sessionCropRegion = getSessionCropRegion(mControlUpdateCallback);
Rect sensorRect = getSensorRect();
int cropX = (sensorRect.width() / 4);
int cropY = (sensorRect.height() / 4);
Rect cropRect = new Rect(cropX, cropY, cropX + sensorRect.width() / 2,
cropY + sensorRect.height() / 2);
assertThat(sessionCropRegion).isEqualTo(cropRect);
}
@NonNull
private Rect getSessionCropRegion(
CameraControlInternal.ControlUpdateCallback controlUpdateCallback)
throws InterruptedException {
verify(controlUpdateCallback, times(1)).onCameraControlUpdateSessionConfig(
mSessionConfigArgumentCaptor.capture());
SessionConfig sessionConfig = mSessionConfigArgumentCaptor.getValue();
Camera2Config camera2Config = new Camera2Config(sessionConfig.getImplementationOptions());
reset(controlUpdateCallback);
return camera2Config.getCaptureRequestOption(CaptureRequest.SCALER_CROP_REGION, null);
}
@Test
public void setZoomPercentage_CropRegionIsUpdatedCorrectly() throws InterruptedException {
assumeTrue(isZoomSupported());
mCamera2CameraControl.setZoomPercentage(1.0f);
HandlerUtil.waitForLooperToIdle(mHandler);
Rect cropRegionMaxZoom = getSessionCropRegion(mControlUpdateCallback);
Rect cropRegionMinZoom = getSensorRect();
mCamera2CameraControl.setZoomPercentage(0.5f);
HandlerUtil.waitForLooperToIdle(mHandler);
Rect cropRegionHalfZoom = getSessionCropRegion(mControlUpdateCallback);
Assert.assertEquals(cropRegionHalfZoom.width(),
(cropRegionMinZoom.width() + cropRegionMaxZoom.width()) / 2.0f, 1
/* 1 pixel tolerance */);
}
@Test
public void setZoomRatio_cameraControlInactive_operationCanceled() {
mCamera2CameraControl.setActive(false);
ListenableFuture<Void> listenableFuture = mCamera2CameraControl.setZoomRatio(2.0f);
try {
listenableFuture.get(1000, TimeUnit.MILLISECONDS);
} catch (ExecutionException e) {
if (e.getCause() instanceof CameraControl.OperationCanceledException) {
assertTrue(true);
return;
}
} catch (Exception e) {
}
fail();
}
@Test
public void setZoomPercentage_cameraControlInactive_operationCanceled() {
mCamera2CameraControl.setActive(false);
ListenableFuture<Void> listenableFuture = mCamera2CameraControl.setZoomPercentage(0.0f);
try {
listenableFuture.get(1000, TimeUnit.MILLISECONDS);
} catch (ExecutionException e) {
if (e.getCause() instanceof CameraControl.OperationCanceledException) {
assertTrue(true);
return;
}
} catch (Exception e) {
}
fail();
}
}