aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorScott Nien <scottnien@google.com>2019-09-24 16:56:08 +0800
committerScott Nien <scottnien@google.com>2019-10-28 12:13:01 +0800
commitf60a1ac99a4144d5bfb0529fa0c769f43eb10c9f (patch)
tree60f5623044f4ad7a3990dd10d0e9cddbec3459f0
parent95b83f80af058444256dfc7925131af8a56fd579 (diff)
downloadsupport-f60a1ac99a4144d5bfb0529fa0c769f43eb10c9f.tar.gz
[Zoom] Expose zoom APIs in CameraControl and CameraInfo, adding active state for FocusMetering APIs.
* Supports setZoomRatio() / setZoomPercentage() in CameraControl. * Supports getZoomRatio() / getZoomPercentage() / getMaxZoomRatio / getMinZoomRatio() in CameraInfo. * CameraControl is active when there are attached online use cases otherwise it is inactive. Calling setZoomRatio/setZoomPercentage/startFocusAndMetering when inactive will do nothing. Test: Camera2CameraControlTest / CameraImplTest / FocusMeteringControlTest Bug: 128986739 Change-Id: I3fc3791583fdf47e70f8fadd255517d07c6da630
-rw-r--r--camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/impl/Camera2CameraControlTest.java108
-rw-r--r--camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/impl/Camera2CameraImplTest.java48
-rw-r--r--camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/Camera2CameraControl.java35
-rw-r--r--camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/Camera2CameraImpl.java14
-rw-r--r--camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/Camera2CameraInfo.java29
-rw-r--r--camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/FocusMeteringControl.java41
-rw-r--r--camera/camera-camera2/src/test/java/androidx/camera/camera2/impl/Camera2CameraInfoTest.java65
-rw-r--r--camera/camera-camera2/src/test/java/androidx/camera/camera2/impl/Camera2DeviceSurfaceManagerTest.java3
-rw-r--r--camera/camera-camera2/src/test/java/androidx/camera/camera2/impl/FocusMeteringControlTest.java7
-rw-r--r--camera/camera-camera2/src/test/java/androidx/camera/camera2/impl/SupportedSurfaceCombinationTest.java3
-rw-r--r--camera/camera-core/src/main/java/androidx/camera/core/CameraControl.java46
-rw-r--r--camera/camera-core/src/main/java/androidx/camera/core/CameraControlInternal.java15
-rw-r--r--camera/camera-core/src/main/java/androidx/camera/core/CameraInfo.java60
-rw-r--r--camera/camera-core/src/test/java/androidx/camera/core/ShadowCameraX.java32
-rw-r--r--camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCameraControl.java15
-rw-r--r--camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCameraInfoInternal.java34
16 files changed, 529 insertions, 26 deletions
diff --git a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/impl/Camera2CameraControlTest.java b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/impl/Camera2CameraControlTest.java
index 31473157473..80828929a13 100644
--- a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/impl/Camera2CameraControlTest.java
+++ b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/impl/Camera2CameraControlTest.java
@@ -30,6 +30,8 @@ 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;
@@ -48,7 +50,9 @@ 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;
@@ -66,7 +70,10 @@ 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;
@@ -74,7 +81,9 @@ 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)
@@ -111,6 +120,7 @@ public final class Camera2CameraControlTest {
mCamera2CameraControl = new Camera2CameraControl(mCameraCharacteristics,
executorService, executorService, mControlUpdateCallback);
+ mCamera2CameraControl.setActive(true);
HandlerUtil.waitForLooperToIdle(mHandler);
// Reset the method call onCameraControlUpdateSessionConfig() in Camera2CameraControl
@@ -627,4 +637,102 @@ public final class Camera2CameraControlTest {
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();
+ }
+
}
diff --git a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/impl/Camera2CameraImplTest.java b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/impl/Camera2CameraImplTest.java
index 94d5754ffd2..8a312cffe07 100644
--- a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/impl/Camera2CameraImplTest.java
+++ b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/impl/Camera2CameraImplTest.java
@@ -44,6 +44,7 @@ import androidx.annotation.Nullable;
import androidx.camera.camera2.impl.compat.CameraManagerCompat;
import androidx.camera.core.CameraCaptureCallback;
import androidx.camera.core.CameraCaptureResult;
+import androidx.camera.core.CameraControl;
import androidx.camera.core.CameraDeviceConfig;
import androidx.camera.core.CameraFactory;
import androidx.camera.core.CameraInternal;
@@ -88,6 +89,8 @@ import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicReference;
/**
@@ -848,6 +851,51 @@ public final class Camera2CameraImplTest {
verify(useCase3, times(0)).onStateOffline(eq(mCameraId));
}
+ private boolean isCameraControlActive(Camera2CameraControl camera2CameraControl) {
+ ListenableFuture<Void> listenableFuture = camera2CameraControl.setZoomRatio(2.0f);
+ try {
+ // setZoom() will fail immediately when Cameracontrol is not active.
+ listenableFuture.get(50, TimeUnit.MILLISECONDS);
+ } catch (ExecutionException e) {
+ if (e.getCause() instanceof CameraControl.OperationCanceledException) {
+ return false;
+ }
+ } catch (InterruptedException | TimeoutException e) {
+ }
+ return true;
+ }
+
+ @Test
+ public void activateCameraControl_whenExsitsOnlineUseCases() throws InterruptedException {
+ Camera2CameraControl camera2CameraControl =
+ (Camera2CameraControl) mCamera2CameraImpl.getCameraControlInternal();
+
+ assertThat(isCameraControlActive(camera2CameraControl)).isFalse();
+
+ UseCase useCase1 = createUseCase();
+
+ mCamera2CameraImpl.addOnlineUseCase(Arrays.asList(useCase1));
+ HandlerUtil.waitForLooperToIdle(mCameraHandler);
+
+ assertThat(isCameraControlActive(camera2CameraControl)).isTrue();
+ }
+
+ @Test
+ public void deactivateCameraControl_whenNoOnlineUseCases() throws InterruptedException {
+ Camera2CameraControl camera2CameraControl =
+ (Camera2CameraControl) mCamera2CameraImpl.getCameraControlInternal();
+ UseCase useCase1 = createUseCase();
+
+ mCamera2CameraImpl.addOnlineUseCase(Arrays.asList(useCase1));
+ HandlerUtil.waitForLooperToIdle(mCameraHandler);
+ assertThat(isCameraControlActive(camera2CameraControl)).isTrue();
+
+ mCamera2CameraImpl.removeOnlineUseCase(Arrays.asList(useCase1));
+ HandlerUtil.waitForLooperToIdle(mCameraHandler);
+
+ assertThat(isCameraControlActive(camera2CameraControl)).isFalse();
+ }
+
private DeferrableSurface getUseCaseSurface(UseCase useCase) {
return useCase.getSessionConfig(mCameraId).getSurfaces().get(0);
}
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/Camera2CameraControl.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/Camera2CameraControl.java
index 58442e3002a..86f1dbdfb49 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/Camera2CameraControl.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/Camera2CameraControl.java
@@ -40,6 +40,8 @@ import androidx.camera.core.FocusMeteringAction;
import androidx.camera.core.SessionConfig;
import androidx.core.util.Preconditions;
+import com.google.common.util.concurrent.ListenableFuture;
+
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
@@ -65,9 +67,11 @@ public final class Camera2CameraControl implements CameraControlInternal {
@SuppressWarnings("WeakerAccess") /* synthetic accessor */
volatile Rational mPreviewAspectRatio = null;
private final FocusMeteringControl mFocusMeteringControl;
+ private final ZoomControl mZoomControl;
// use volatile modifier to make these variables in sync in all threads.
private volatile boolean mIsTorchOn = false;
private volatile FlashMode mFlashMode = FlashMode.OFF;
+ private volatile boolean mIsActive = false;
//******************** Should only be accessed by executor *****************************//
private Rect mCropRect = null;
@@ -98,11 +102,30 @@ public final class Camera2CameraControl implements CameraControlInternal {
CaptureCallbackContainer.create(mSessionCallback));
mFocusMeteringControl = new FocusMeteringControl(this, scheduler, mExecutor);
+ mZoomControl = new ZoomControl(this, mCameraCharacteristics);
// Initialize the session config
mExecutor.execute(this::updateSessionConfig);
}
+ @NonNull
+ public ZoomControl getZoomControl() {
+ return mZoomControl;
+ }
+
+ /**
+ * Set current active state. Set active if it is ready to trigger camera control operation.
+ *
+ * <p>Most operations during inactive state do nothing. Some states are reset to default
+ * once it is changed to inactive state.
+ */
+ void setActive(boolean isActive) {
+ mIsActive = isActive;
+ mFocusMeteringControl.setActive(isActive);
+ mZoomControl.setActive(isActive);
+ }
+
+ @WorkerThread
public void setPreviewAspectRatio(@Nullable Rational previewAspectRatio) {
mPreviewAspectRatio = previewAspectRatio;
}
@@ -118,6 +141,18 @@ public final class Camera2CameraControl implements CameraControlInternal {
mExecutor.execute(mFocusMeteringControl::cancelFocusAndMetering);
}
+ @NonNull
+ @Override
+ public ListenableFuture<Void> setZoomRatio(float ratio) {
+ return mZoomControl.setZoomRatio(ratio);
+ }
+
+ @NonNull
+ @Override
+ public ListenableFuture<Void> setZoomPercentage(float percentage) {
+ return mZoomControl.setZoomPercentage(percentage);
+ }
+
/** {@inheritDoc} */
@Override
public void setCropRegion(@Nullable final Rect crop) {
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/Camera2CameraImpl.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/Camera2CameraImpl.java
index b656f64410e..ec589e51a8a 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/Camera2CameraImpl.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/Camera2CameraImpl.java
@@ -713,6 +713,9 @@ final class Camera2CameraImpl implements CameraInternal {
}
}
+ // CameraControl is active when there are any online use cases.
+ mCameraControlInternal.setActive(true);
+
if (Looper.myLooper() != mHandler.getLooper()) {
mHandler.post(new Runnable() {
@Override
@@ -768,7 +771,6 @@ final class Camera2CameraImpl implements CameraInternal {
});
}
-
private void updateCameraControlPreviewAspectRatio(Collection<UseCase> useCases) {
for (UseCase useCase : useCases) {
if (useCase instanceof Preview) {
@@ -780,8 +782,8 @@ final class Camera2CameraImpl implements CameraInternal {
}
}
- private void clearCameraControlPreviewAspectRatio(Collection<UseCase> useCases) {
- for (UseCase useCase : useCases) {
+ private void clearCameraControlPreviewAspectRatio(Collection<UseCase> removedUseCases) {
+ for (UseCase useCase : removedUseCases) {
if (useCase instanceof Preview) {
mCameraControlInternal.setPreviewAspectRatio(null);
return;
@@ -810,6 +812,7 @@ final class Camera2CameraImpl implements CameraInternal {
}
Log.d(TAG, "Use cases " + useCases + " OFFLINE for camera " + mCameraId);
+ clearCameraControlPreviewAspectRatio(useCases);
synchronized (mAttachedUseCaseLock) {
List<UseCase> useCasesChangedToOffline = new ArrayList<>();
for (UseCase useCase : useCases) {
@@ -826,6 +829,7 @@ final class Camera2CameraImpl implements CameraInternal {
notifyStateOfflineToUseCases(useCasesChangedToOffline);
if (mUseCaseAttachState.getOnlineUseCases().isEmpty()) {
+ mCameraControlInternal.setActive(false);
resetCaptureSession(/*abortInFlightCaptures=*/false);
close();
return;
@@ -839,7 +843,6 @@ final class Camera2CameraImpl implements CameraInternal {
openCaptureSession();
}
- clearCameraControlPreviewAspectRatio(useCases);
}
/** Returns an interface to retrieve characteristics of the camera. */
@@ -849,7 +852,8 @@ final class Camera2CameraImpl implements CameraInternal {
synchronized (mCameraInfoLock) {
if (mCameraInfoInternal == null) {
// Lazily instantiate camera info
- mCameraInfoInternal = new Camera2CameraInfo(mCameraManager.unwrap(), mCameraId);
+ mCameraInfoInternal = new Camera2CameraInfo(mCameraManager.unwrap(), mCameraId,
+ mCameraControlInternal.getZoomControl());
}
return mCameraInfoInternal;
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/Camera2CameraInfo.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/Camera2CameraInfo.java
index 272691f227a..4add6295c5b 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/Camera2CameraInfo.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/Camera2CameraInfo.java
@@ -40,10 +40,13 @@ import androidx.lifecycle.MutableLiveData;
final class Camera2CameraInfo implements CameraInfoInternal {
private final CameraCharacteristics mCameraCharacteristics;
+ private final ZoomControl mZoomControl;
private static final String TAG = "Camera2CameraInfo";
private MutableLiveData<Boolean> mFlashAvailability;
- Camera2CameraInfo(CameraManager cameraManager, String cameraId)
+
+ Camera2CameraInfo(@NonNull CameraManager cameraManager, @NonNull String cameraId,
+ @NonNull ZoomControl zoomControl)
throws CameraInfoUnavailableException {
try {
mCameraCharacteristics = cameraManager.getCameraCharacteristics(cameraId);
@@ -52,6 +55,7 @@ final class Camera2CameraInfo implements CameraInfoInternal {
"Unable to retrieve info for camera " + cameraId, e);
}
+ mZoomControl = zoomControl;
mFlashAvailability = new MutableLiveData<>(
mCameraCharacteristics.get(CameraCharacteristics.FLASH_INFO_AVAILABLE));
checkCharacteristicAvailable(
@@ -158,4 +162,27 @@ final class Camera2CameraInfo implements CameraInfoInternal {
return mFlashAvailability;
}
+ @NonNull
+ @Override
+ public LiveData<Float> getZoomRatio() {
+ return mZoomControl.getZoomRatio();
+ }
+
+ @NonNull
+ @Override
+ public LiveData<Float> getMaxZoomRatio() {
+ return mZoomControl.getMaxZoomRatio();
+ }
+
+ @NonNull
+ @Override
+ public LiveData<Float> getMinZoomRatio() {
+ return mZoomControl.getMinZoomRatio();
+ }
+
+ @NonNull
+ @Override
+ public LiveData<Float> getZoomPercentage() {
+ return mZoomControl.getZoomPercentage();
+ }
}
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/FocusMeteringControl.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/FocusMeteringControl.java
index d34d9a8b47d..3e7eec67c1a 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/FocusMeteringControl.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/impl/FocusMeteringControl.java
@@ -23,6 +23,7 @@ import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.CaptureResult;
import android.hardware.camera2.params.MeteringRectangle;
import android.os.Build;
+import android.util.Log;
import android.util.Rational;
import androidx.annotation.NonNull;
@@ -61,12 +62,15 @@ import java.util.concurrent.TimeUnit;
* construct the 3A regions and append them to all repeating requests and single requests.
*/
class FocusMeteringControl {
+ private static final String TAG = "FocusMeteringControl";
private final Camera2CameraControl mCameraControl;
@SuppressWarnings("WeakerAccess") /* synthetic accessor */
@CameraExecutor
final Executor mExecutor;
private final ScheduledExecutorService mScheduler;
+ private volatile boolean mIsActive = false;
+
//******************** Should only be accessed by executor (WorkThread) ****************//
private FocusMeteringAction mCurrentFocusMeteringAction;
private boolean mIsInAfAutoMode = false;
@@ -99,6 +103,25 @@ class FocusMeteringControl {
}
/**
+ * Set current active state. Set active if it is ready to accept focus/metering operations.
+ *
+ * <p> In inactive state, startFocusAndMetering does nothing while cancelFocusAndMetering
+ * still works to cancel current operation. cancelFocusAndMetering is performed automatically
+ * when active state is changed to false.
+ */
+ void setActive(boolean isActive) {
+ if (isActive == mIsActive) {
+ return;
+ }
+
+ mIsActive = isActive;
+
+ if (!mIsActive) {
+ mExecutor.execute(() -> cancelFocusAndMetering());
+ }
+ }
+
+ /**
* Called by {@link Camera2CameraControl} to append the 3A regions to the shared options. It
* applies to all repeating requests and single requests.
*/
@@ -111,7 +134,6 @@ class FocusMeteringControl {
configBuilder.setCaptureRequestOption(
CaptureRequest.CONTROL_AF_MODE, mCameraControl.getSupportedAfMode(afMode));
-
if (mAfRects.length != 0) {
configBuilder.setCaptureRequestOption(
CaptureRequest.CONTROL_AF_REGIONS, mAfRects);
@@ -193,6 +215,11 @@ class FocusMeteringControl {
@WorkerThread
void startFocusAndMetering(@NonNull FocusMeteringAction action,
@Nullable Rational defaultAspectRatio) {
+ if (!mIsActive) {
+ Log.e(TAG, "Ignore startFocusAndMetering because camera is not active.");
+ return;
+ }
+
if (mCurrentFocusMeteringAction != null) {
cancelFocusAndMetering();
}
@@ -251,6 +278,10 @@ class FocusMeteringControl {
@WorkerThread
void triggerAf() {
+ if (!mIsActive) {
+ return;
+ }
+
CaptureConfig.Builder builder = new CaptureConfig.Builder();
builder.setTemplateType(getDefaultTemplate());
builder.setUseRepeatingSurface(true);
@@ -263,6 +294,10 @@ class FocusMeteringControl {
@WorkerThread
void triggerAePrecapture() {
+ if (!mIsActive) {
+ return;
+ }
+
CaptureConfig.Builder builder = new CaptureConfig.Builder();
builder.setTemplateType(getDefaultTemplate());
builder.setUseRepeatingSurface(true);
@@ -276,6 +311,10 @@ class FocusMeteringControl {
@WorkerThread
void cancelAfAeTrigger(final boolean cancelAfTrigger,
final boolean cancelAePrecaptureTrigger) {
+ if (!mIsActive) {
+ return;
+ }
+
CaptureConfig.Builder builder = new CaptureConfig.Builder();
builder.setUseRepeatingSurface(true);
builder.setTemplateType(getDefaultTemplate());
diff --git a/camera/camera-camera2/src/test/java/androidx/camera/camera2/impl/Camera2CameraInfoTest.java b/camera/camera-camera2/src/test/java/androidx/camera/camera2/impl/Camera2CameraInfoTest.java
index 56849432570..b1b89c545b1 100644
--- a/camera/camera-camera2/src/test/java/androidx/camera/camera2/impl/Camera2CameraInfoTest.java
+++ b/camera/camera-camera2/src/test/java/androidx/camera/camera2/impl/Camera2CameraInfoTest.java
@@ -18,6 +18,9 @@ package androidx.camera.camera2.impl;
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
import android.content.Context;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraManager;
@@ -27,6 +30,7 @@ import android.view.Surface;
import androidx.camera.core.CameraInfoInternal;
import androidx.camera.core.CameraInfoUnavailableException;
import androidx.camera.core.LensFacing;
+import androidx.lifecycle.MutableLiveData;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.filters.SmallTest;
@@ -73,13 +77,15 @@ public class Camera2CameraInfoTest {
@Test
public void canCreateCameraInfo() throws CameraInfoUnavailableException {
- CameraInfoInternal cameraInfoInternal = new Camera2CameraInfo(mCameraManager, CAMERA0_ID);
+ CameraInfoInternal cameraInfoInternal = new Camera2CameraInfo(mCameraManager, CAMERA0_ID,
+ mock(ZoomControl.class));
assertThat(cameraInfoInternal).isNotNull();
}
@Test
public void cameraInfo_canReturnSensorOrientation() throws CameraInfoUnavailableException {
- CameraInfoInternal cameraInfoInternal = new Camera2CameraInfo(mCameraManager, CAMERA0_ID);
+ CameraInfoInternal cameraInfoInternal = new Camera2CameraInfo(mCameraManager, CAMERA0_ID,
+ mock(ZoomControl.class));
assertThat(cameraInfoInternal.getSensorRotationDegrees()).isEqualTo(
CAMERA0_SENSOR_ORIENTATION);
}
@@ -87,7 +93,8 @@ public class Camera2CameraInfoTest {
@Test
public void cameraInfo_canCalculateCorrectRelativeRotation_forBackCamera()
throws CameraInfoUnavailableException {
- CameraInfoInternal cameraInfoInternal = new Camera2CameraInfo(mCameraManager, CAMERA0_ID);
+ CameraInfoInternal cameraInfoInternal = new Camera2CameraInfo(mCameraManager, CAMERA0_ID,
+ mock(ZoomControl.class));
// Note: these numbers depend on the camera being a back-facing camera.
assertThat(cameraInfoInternal.getSensorRotationDegrees(Surface.ROTATION_0))
@@ -103,7 +110,8 @@ public class Camera2CameraInfoTest {
@Test
public void cameraInfo_canCalculateCorrectRelativeRotation_forFrontCamera()
throws CameraInfoUnavailableException {
- CameraInfoInternal cameraInfoInternal = new Camera2CameraInfo(mCameraManager, CAMERA1_ID);
+ CameraInfoInternal cameraInfoInternal = new Camera2CameraInfo(mCameraManager, CAMERA1_ID,
+ mock(ZoomControl.class));
// Note: these numbers depend on the camera being a front-facing camera.
assertThat(cameraInfoInternal.getSensorRotationDegrees(Surface.ROTATION_0))
@@ -118,14 +126,16 @@ public class Camera2CameraInfoTest {
@Test
public void cameraInfo_canReturnLensFacing() throws CameraInfoUnavailableException {
- CameraInfoInternal cameraInfoInternal = new Camera2CameraInfo(mCameraManager, CAMERA0_ID);
+ CameraInfoInternal cameraInfoInternal = new Camera2CameraInfo(mCameraManager, CAMERA0_ID,
+ mock(ZoomControl.class));
assertThat(cameraInfoInternal.getLensFacing()).isEqualTo(CAMERA0_LENS_FACING_ENUM);
}
@Test
public void cameraInfo_canReturnFlashAvailable_forBackCamera()
throws CameraInfoUnavailableException {
- CameraInfoInternal cameraInfoInternal = new Camera2CameraInfo(mCameraManager, CAMERA0_ID);
+ CameraInfoInternal cameraInfoInternal = new Camera2CameraInfo(mCameraManager, CAMERA0_ID,
+ mock(ZoomControl.class));
assertThat(cameraInfoInternal.isFlashAvailable().getValue().booleanValue()).isEqualTo(
CAMERA0_FLASH_INFO_BOOLEAN);
}
@@ -133,11 +143,52 @@ public class Camera2CameraInfoTest {
@Test
public void cameraInfo_canReturnFlashAvailable_forFrontCamera()
throws CameraInfoUnavailableException {
- CameraInfoInternal cameraInfoInternal = new Camera2CameraInfo(mCameraManager, CAMERA1_ID);
+ CameraInfoInternal cameraInfoInternal = new Camera2CameraInfo(mCameraManager, CAMERA1_ID,
+ mock(ZoomControl.class));
assertThat(cameraInfoInternal.isFlashAvailable().getValue().booleanValue()).isEqualTo(
CAMERA1_FLASH_INFO_BOOLEAN);
}
+ // zoom related tests just ensure it uses ZoomControl to get the value
+ // Full tests are performed at ZoomControlTest / ZoomControlRoboTest.
+ @Test
+ public void cameraInfo_getZoomRatio_valueIsCorrect() throws CameraInfoUnavailableException {
+ ZoomControl zoomControl = mock(ZoomControl.class);
+ CameraInfoInternal cameraInfo = new Camera2CameraInfo(mCameraManager, CAMERA0_ID,
+ zoomControl);
+ when(zoomControl.getZoomRatio()).thenReturn(new MutableLiveData<>(3.0f));
+ assertThat(cameraInfo.getZoomRatio().getValue()).isEqualTo(3.0f);
+ }
+
+ @Test
+ public void cameraInfo_getZoomPercentage_valueIsCorrect()
+ throws CameraInfoUnavailableException {
+ ZoomControl zoomControl = mock(ZoomControl.class);
+ CameraInfoInternal cameraInfo = new Camera2CameraInfo(mCameraManager, CAMERA0_ID,
+ zoomControl);
+ when(zoomControl.getZoomPercentage()).thenReturn(new MutableLiveData<>(0.2f));
+ assertThat(cameraInfo.getZoomPercentage().getValue()).isEqualTo(0.2f);
+ }
+
+ @Test
+ public void cameraInfo_getMaxZoomRatio_valueIsCorrect() throws CameraInfoUnavailableException {
+ ZoomControl zoomControl = mock(ZoomControl.class);
+ CameraInfoInternal cameraInfo = new Camera2CameraInfo(mCameraManager, CAMERA0_ID,
+ zoomControl);
+ when(zoomControl.getMaxZoomRatio()).thenReturn(new MutableLiveData<>(8.0f));
+ assertThat(cameraInfo.getMaxZoomRatio().getValue()).isEqualTo(8.0f);
+ }
+
+ @Test
+ public void cameraInfo_getMinZoomRatio_valueIsCorrect() throws CameraInfoUnavailableException {
+ ZoomControl zoomControl = mock(ZoomControl.class);
+ CameraInfoInternal cameraInfo = new Camera2CameraInfo(mCameraManager, CAMERA0_ID,
+ zoomControl);
+ when(zoomControl.getMinZoomRatio()).thenReturn(new MutableLiveData<>(1.0f));
+ assertThat(cameraInfo.getMinZoomRatio().getValue()).isEqualTo(1.0f);
+ }
+
+
private void initCameras() {
// **** Camera 0 characteristics ****//
CameraCharacteristics characteristics0 =
diff --git a/camera/camera-camera2/src/test/java/androidx/camera/camera2/impl/Camera2DeviceSurfaceManagerTest.java b/camera/camera-camera2/src/test/java/androidx/camera/camera2/impl/Camera2DeviceSurfaceManagerTest.java
index 2c461e71566..cfcc36052c9 100644
--- a/camera/camera-camera2/src/test/java/androidx/camera/camera2/impl/Camera2DeviceSurfaceManagerTest.java
+++ b/camera/camera-camera2/src/test/java/androidx/camera/camera2/impl/Camera2DeviceSurfaceManagerTest.java
@@ -23,6 +23,7 @@ import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
import static org.mockito.Matchers.anyInt;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import android.content.Context;
@@ -580,7 +581,7 @@ public final class Camera2DeviceSurfaceManagerTest {
LensFacing lensFacingEnum = CameraUtil.getLensFacingEnumFromInt(lensFacing);
mCameraFactory.insertCamera(lensFacingEnum, cameraId, () -> new FakeCamera(cameraId, null,
- new Camera2CameraInfo(cameraManager, cameraId)));
+ new Camera2CameraInfo(cameraManager, cameraId, mock(ZoomControl.class))));
}
private void initCameraX() {
diff --git a/camera/camera-camera2/src/test/java/androidx/camera/camera2/impl/FocusMeteringControlTest.java b/camera/camera-camera2/src/test/java/androidx/camera/camera2/impl/FocusMeteringControlTest.java
index 0b4ede7efd1..43e06a2e358 100644
--- a/camera/camera-camera2/src/test/java/androidx/camera/camera2/impl/FocusMeteringControlTest.java
+++ b/camera/camera-camera2/src/test/java/androidx/camera/camera2/impl/FocusMeteringControlTest.java
@@ -121,6 +121,7 @@ public class FocusMeteringControlTest {
public void setUp() throws CameraAccessException {
initCameras();
mFocusMeteringControl = spy(initFocusMeteringControl(CAMERA0_ID));
+ mFocusMeteringControl.setActive(true);
}
@After
@@ -149,9 +150,10 @@ public class FocusMeteringControlTest {
mCameraExecutor,
updateCallback));
-
- return new FocusMeteringControl(mCamera2CameraControl,
+ FocusMeteringControl focusMeteringControl = new FocusMeteringControl(mCamera2CameraControl,
CameraXExecutors.mainThreadExecutor(), mCameraExecutor);
+ focusMeteringControl.setActive(true);
+ return focusMeteringControl;
}
private void initCameras() {
@@ -896,5 +898,4 @@ public class FocusMeteringControlTest {
mFocusMeteringControl.cancelFocusAndMetering();
verifyAfMode(CaptureResult.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
}
-
}
diff --git a/camera/camera-camera2/src/test/java/androidx/camera/camera2/impl/SupportedSurfaceCombinationTest.java b/camera/camera-camera2/src/test/java/androidx/camera/camera2/impl/SupportedSurfaceCombinationTest.java
index c5ed8d5a29b..c9bce8128a0 100644
--- a/camera/camera-camera2/src/test/java/androidx/camera/camera2/impl/SupportedSurfaceCombinationTest.java
+++ b/camera/camera-camera2/src/test/java/androidx/camera/camera2/impl/SupportedSurfaceCombinationTest.java
@@ -23,6 +23,7 @@ import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
import static org.mockito.Matchers.anyInt;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import android.content.Context;
@@ -1122,7 +1123,7 @@ public final class SupportedSurfaceCombinationTest {
LensFacing lensFacingEnum = CameraUtil.getLensFacingEnumFromInt(lensFacing);
mCameraFactory.insertCamera(lensFacingEnum, cameraId, () -> new FakeCamera(cameraId, null,
- new Camera2CameraInfo(cameraManager, cameraId)));
+ new Camera2CameraInfo(cameraManager, cameraId, mock(ZoomControl.class))));
}
private void initCameraX() {
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/CameraControl.java b/camera/camera-core/src/main/java/androidx/camera/core/CameraControl.java
index 28a86fc07d0..85e4780de16 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/CameraControl.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/CameraControl.java
@@ -16,10 +16,13 @@
package androidx.camera.core;
+import androidx.annotation.FloatRange;
import androidx.annotation.NonNull;
import androidx.annotation.RestrictTo;
import androidx.camera.core.FocusMeteringAction.OnAutoFocusListener;
+import com.google.common.util.concurrent.ListenableFuture;
+
/**
* An interface for controlling camera's zoom, focus and metering across all use cases.
*
@@ -29,8 +32,8 @@ public interface CameraControl {
/**
* Starts a focus and metering action by the {@link FocusMeteringAction}.
*
- * The {@link FocusMeteringAction} contains the configuration of multiple 3A
- * {@link MeteringPoint}s, auto-cancel duration and{@link OnAutoFocusListener} to receive the
+ * <p>The {@link FocusMeteringAction} contains the configuration of multiple 3A
+ * {@link MeteringPoint}s, auto-cancel duration and{ @link OnAutoFocusListener} to receive the
* auto-focus result. Check {@link FocusMeteringAction} for more details.
*
* @param action the {@link FocusMeteringAction} to be executed.
@@ -41,12 +44,49 @@ public interface CameraControl {
* Cancels current {@link FocusMeteringAction}.
*
* <p>It clears the 3A regions and update current AF mode to CONTINOUS AF (if supported).
- * If auto-focus does not completes, it will notify the {@link OnAutoFocusListener} with
+ * If auto-focus does not complete, it will notify the {@link OnAutoFocusListener} with
* isFocusLocked set to false.
*/
void cancelFocusAndMetering();
/**
+ * Sets current zoom by ratio.
+ *
+ * <p>It modifies both current zoom ratio and zoom percentage so if apps are observing
+ * zoomRatio or zoomPercentage, they will get the update as well. If the ratio is
+ * smaller than {@link CameraInfo#getMinZoomRatio()} or larger than
+ * {@link CameraInfo#getMaxZoomRatio()}, it won't modify current zoom ratio. It is
+ * applications' duty to clamp the ratio.
+ *
+ * @return a {@link ListenableFuture} which is finished when current repeating request
+ * result contains the requested zoom ratio. It fails with
+ * {@link OperationCanceledException} if there is newer value being set or camera is closed.
+ * If ratio is out of range, it fails with
+ * {@link CameraControl.ArgumentOutOfRangeException}.
+ */
+ @NonNull
+ ListenableFuture<Void> setZoomRatio(float ratio);
+
+ /**
+ * Sets current zoom by percentage ranging from 0f to 1.0f. Percentage 0f represents the
+ * minimum zoom while percentage 1.0f represents the maximum zoom. One advantage of zoom
+ * percentage is that it ensures FOV varies linearly with the percentage value.
+ *
+ * <p>It modifies both current zoom ratio and zoom percentage so if apps are observing
+ * zoomRatio or zoomPercentage, they will get the update as well. If the percentage is not in
+ * the range [0..1], it won't modify current zoom percentage and zoom ratio. It is
+ * applications' duty to clamp the zoomPercentage within [0..1].
+ *
+ * @return a {@link ListenableFuture} which is finished when current repeating request
+ * result contains the requested zoom percentage. It fails with
+ * {@link OperationCanceledException} if there is newer value being set or camera is closed.
+ * If percentage is out of range, it fails with
+ * {@link CameraControl.ArgumentOutOfRangeException}.
+ */
+ @NonNull
+ ListenableFuture<Void> setZoomPercentage(@FloatRange(from = 0f, to = 1f) float percentage);
+
+ /**
* An exception thrown when the argument is out of range.
*/
final class ArgumentOutOfRangeException extends Exception {
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/CameraControlInternal.java b/camera/camera-core/src/main/java/androidx/camera/core/CameraControlInternal.java
index 989b89d788a..e5e60ee1d5a 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/CameraControlInternal.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/CameraControlInternal.java
@@ -22,6 +22,9 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
import androidx.annotation.RestrictTo.Scope;
+import androidx.camera.core.impl.utils.futures.Futures;
+
+import com.google.common.util.concurrent.ListenableFuture;
import java.util.List;
@@ -131,6 +134,18 @@ public interface CameraControlInternal extends CameraControl {
public void cancelFocusAndMetering() {
}
+
+ @NonNull
+ @Override
+ public ListenableFuture<Void> setZoomRatio(float ratio) {
+ return Futures.immediateFuture(null);
+ }
+
+ @NonNull
+ @Override
+ public ListenableFuture<Void> setZoomPercentage(float percentage) {
+ return Futures.immediateFuture(null);
+ }
};
/** Listener called when CameraControlInternal need to notify event. */
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/CameraInfo.java b/camera/camera-core/src/main/java/androidx/camera/core/CameraInfo.java
index 9d42f84f0d7..ddfb0a0afa9 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/CameraInfo.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/CameraInfo.java
@@ -19,7 +19,9 @@ package androidx.camera.core;
import android.view.Surface;
import androidx.annotation.NonNull;
+import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.LiveData;
+import androidx.lifecycle.Observer;
/**
* An interface for retrieving camera information.
@@ -50,4 +52,62 @@ public interface CameraInfo {
/** Returns if flash unit is available or not. */
@NonNull
LiveData<Boolean> isFlashAvailable();
+
+ /**
+ * Returns a {@link LiveData} of current zoom ratio.
+ *
+ * <p>Apps can either get immediate value via {@link LiveData#getValue()} (The value is never
+ * null, it has default value in the beginning) or they can observe it via
+ * {@link LiveData#observe(LifecycleOwner, Observer)} to update zoom UI accordingly.
+ *
+ * <p>Setting zoom ratio or zoom percentage will both trigger the change event.
+ *
+ * @return a {@link LiveData} containing current zoom ratio.
+ */
+ @NonNull
+ LiveData<Float> getZoomRatio();
+
+ /**
+ * Returns a {@link LiveData} of the maximum zoom ratio.
+ *
+ * <p>Apps can either get immediate value via {@link LiveData#getValue()} (The value is never
+ * null, it has default value in the beginning) or they can observe it via
+ * {@link LiveData#observe(LifecycleOwner, Observer)} to update zoom UI accordingly.
+ *
+ * <p>While the value is fixed most of the time, enabling extension could change the maximum
+ * zoom ratio.
+ *
+ * @return a {@link LiveData} containing the maximum zoom ratio value.
+ */
+ @NonNull
+ LiveData<Float> getMaxZoomRatio();
+
+ /**
+ * Returns a {@link LiveData} of the minimum zoom ratio.
+ *
+ * <p>Apps can either get immediate value via {@link LiveData#getValue()} (The value is never
+ * null, it has default value in the beginning) or they can observe it via
+ * {@link LiveData#observe(LifecycleOwner, Observer)} to update zoom UI accordingly.
+ *
+ * <p>While the value is fixed most of the time, enabling extension could change the minimum
+ * zoom ratio value.
+ *
+ * @return a {@link LiveData} containing the minimum zoom ratio value.
+ */
+ @NonNull
+ LiveData<Float> getMinZoomRatio();
+
+ /**
+ * Returns a {@link LiveData} of current zoom percentage which is in range [0..1].
+ * Percentage 0 represents the maximum zoom while percentage 1.0 represents the maximum zoom.
+ *
+ * <p>Apps can either get immediate value via {@link LiveData#getValue()} (The value is never
+ * null, it has default value in the beginning) or they can observe it via
+ * {@link LiveData#observe(LifecycleOwner, Observer)} to update zoom UI accordingly.
+ * <p>Setting zoom ratio or zoom percentage will both trigger the change event.
+ *
+ * @return a {@link LiveData} containing current zoom percentage.
+ */
+ @NonNull
+ LiveData<Float> getZoomPercentage();
}
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/ShadowCameraX.java b/camera/camera-core/src/test/java/androidx/camera/core/ShadowCameraX.java
index c117a72d43f..0cbda8fb131 100644
--- a/camera/camera-core/src/test/java/androidx/camera/core/ShadowCameraX.java
+++ b/camera/camera-core/src/test/java/androidx/camera/core/ShadowCameraX.java
@@ -35,13 +35,19 @@ public class ShadowCameraX {
new ImageAnalysisConfig.Builder().setSessionOptionUnpacker(
new SessionConfig.OptionUnpacker() {
@Override
- public void unpack(UseCaseConfig<?> config, SessionConfig.Builder builder) {
+ public void unpack(@NonNull UseCaseConfig<?> config,
+ @NonNull SessionConfig.Builder builder) {
// no op.
}
}).build();
private static final CameraInfo DEFAULT_CAMERA_INFO = new CameraInfoInternal() {
MutableLiveData<Boolean> mFlashAvailability = new MutableLiveData<>(Boolean.TRUE);
+ MutableLiveData<Float> mZoomRatio = new MutableLiveData<>(1.0f);
+ MutableLiveData<Float> mMaxZoomRatio = new MutableLiveData<>(4.0f);
+ MutableLiveData<Float> mMinZoomRatio = new MutableLiveData<>(1.0f);
+ MutableLiveData<Float> mZoomPercentage = new MutableLiveData<>(0f);
+
@Override
public LensFacing getLensFacing() {
return LensFacing.BACK;
@@ -62,6 +68,30 @@ public class ShadowCameraX {
public LiveData<Boolean> isFlashAvailable() {
return mFlashAvailability;
}
+
+ @NonNull
+ @Override
+ public LiveData<Float> getZoomRatio() {
+ return mZoomRatio;
+ }
+
+ @NonNull
+ @Override
+ public LiveData<Float> getMaxZoomRatio() {
+ return mMaxZoomRatio;
+ }
+
+ @NonNull
+ @Override
+ public LiveData<Float> getMinZoomRatio() {
+ return mMinZoomRatio;
+ }
+
+ @NonNull
+ @Override
+ public LiveData<Float> getZoomPercentage() {
+ return mZoomPercentage;
+ }
};
private static final CameraDeviceSurfaceManager DEFAULT_DEVICE_SURFACE_MANAGER =
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCameraControl.java b/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCameraControl.java
index a107557f6a9..d1dae01edfc 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCameraControl.java
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCameraControl.java
@@ -29,6 +29,9 @@ import androidx.camera.core.CaptureConfig;
import androidx.camera.core.FlashMode;
import androidx.camera.core.FocusMeteringAction;
import androidx.camera.core.SessionConfig;
+import androidx.camera.core.impl.utils.futures.Futures;
+
+import com.google.common.util.concurrent.ListenableFuture;
import java.util.ArrayList;
import java.util.List;
@@ -157,6 +160,18 @@ public final class FakeCameraControl implements CameraControlInternal {
mOnNewCaptureRequestListener = listener;
}
+ @NonNull
+ @Override
+ public ListenableFuture<Void> setZoomRatio(float ratio) {
+ return Futures.immediateFuture(null);
+ }
+
+ @NonNull
+ @Override
+ public ListenableFuture<Void> setZoomPercentage(float percentage) {
+ return Futures.immediateFuture(null);
+ }
+
/** A listener which are used to notify when there are new submitted capture requests */
public interface OnNewCaptureRequestListener {
/** Called when there are new submitted capture request */
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCameraInfoInternal.java b/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCameraInfoInternal.java
index b8de8d5cf49..77fffb2b0e0 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCameraInfoInternal.java
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeCameraInfoInternal.java
@@ -36,17 +36,21 @@ public final class FakeCameraInfoInternal implements CameraInfoInternal {
private final int mSensorRotation;
private final LensFacing mLensFacing;
- private MutableLiveData<Boolean> mFlashAvailability;
+ private MutableLiveData<Boolean> mFlashAvailability = new MutableLiveData<>(Boolean.TRUE);
+ private MutableLiveData<Float> mMaxZoom = new MutableLiveData<>(4.0f);
+ private MutableLiveData<Float> mMinZoom = new MutableLiveData<>(1.0f);
+ private MutableLiveData<Float> mZoomRatio = new MutableLiveData<>(1.0f);
+ private MutableLiveData<Float> mZoomPerecentage = new MutableLiveData<>(0f);
+
+
public FakeCameraInfoInternal() {
this(/*sensorRotation=*/ 0, /*lensFacing=*/ LensFacing.BACK);
- mFlashAvailability = new MutableLiveData<>(Boolean.TRUE);
}
public FakeCameraInfoInternal(int sensorRotation, @NonNull LensFacing lensFacing) {
mSensorRotation = sensorRotation;
mLensFacing = lensFacing;
- mFlashAvailability = new MutableLiveData<>(Boolean.TRUE);
}
@Nullable
@@ -79,4 +83,28 @@ public final class FakeCameraInfoInternal implements CameraInfoInternal {
public LiveData<Boolean> isFlashAvailable() {
return mFlashAvailability;
}
+
+ @NonNull
+ @Override
+ public LiveData<Float> getZoomRatio() {
+ return mZoomRatio;
+ }
+
+ @NonNull
+ @Override
+ public LiveData<Float> getMaxZoomRatio() {
+ return mMaxZoom;
+ }
+
+ @NonNull
+ @Override
+ public LiveData<Float> getMinZoomRatio() {
+ return mMinZoom;
+ }
+
+ @NonNull
+ @Override
+ public LiveData<Float> getZoomPercentage() {
+ return mZoomPerecentage;
+ }
}