[BasicExtenderRefactor] Support Tap-To-Focus and Flash on Extensions with SessionProcessor
1. add startTrigger to SessionProcessor and implement it in
BasicExtenderSessionProcessor
2. modify ProcessingCaptureSession.issueCaptureRequests:
(a) support trigger type request
(b) allows concurrent execution if it is supported
by SessionProcessor.
3. Fix BasicExtenderSessionProcessor does not remove the
parameters that no longer exists.
After the change, tap-to-focus (focusAndMetering), zoom,
flash , torch and exposure compensation works normally with
BasicExtenderSessionProcessor. AdvancedSessionProcessor support
will be added when CameraX support extensions-interface v1.3
Bug: 258963943
Test: ProcessingCaptureSessionTest
Change-Id: I5254d20bb73ccf69df10a1e90df9a09210f0c705
diff --git a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/ProcessingCaptureSessionTest.kt b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/ProcessingCaptureSessionTest.kt
index 37e02a8..24d785a 100644
--- a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/ProcessingCaptureSessionTest.kt
+++ b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/internal/ProcessingCaptureSessionTest.kt
@@ -91,7 +91,7 @@
*/
@LargeTest
@RunWith(Parameterized::class)
-@SdkSuppress(minSdkVersion = 28) // ImageWriter to PRIVATE format requires API 28
+@SdkSuppress(minSdkVersion = 28) // ImageWriter to PRIVATE format requires API 28
class ProcessingCaptureSessionTest(
private var lensFacing: Int,
// The pair specifies (Output image format to Input image format). SessionProcessor will
@@ -310,8 +310,10 @@
)
// Assert
+ sessionProcessor.assertStartCaptureInvoked()
sessionConfigParameters.assertStillCaptureCompleted()
sessionConfigParameters.assertCaptureImageReceived()
+
val parametersConfig = sessionProcessor.getLatestParameters()
assertThat(
parametersConfig.isParameterSet(
@@ -323,6 +325,41 @@
).isTrue()
}
+ @Test
+ fun canIssueAfTrigger(): Unit = runBlocking(Dispatchers.Main) {
+ assertCanIssueTriggerRequest(CaptureRequest.CONTROL_AF_TRIGGER,
+ CaptureRequest.CONTROL_AF_TRIGGER_START)
+ }
+
+ @Test
+ fun canIssueAePrecaptureTrigger(): Unit = runBlocking(Dispatchers.Main) {
+ assertCanIssueTriggerRequest(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
+ CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START)
+ }
+
+ private suspend fun <T : Any> assertCanIssueTriggerRequest(
+ testKey: CaptureRequest.Key<T>,
+ testValue: T
+ ) {
+ // Arrange
+ val cameraDevice = cameraDeviceHolder.get()!!
+ val captureSession = createProcessingCaptureSession()
+ captureSession.open(
+ sessionConfigParameters.getSessionConfigForOpen(), cameraDevice,
+ captureSessionOpenerBuilder.build()
+ ).awaitWithTimeout(3000)
+
+ // Act
+ captureSession.issueCaptureRequests(
+ listOf(sessionConfigParameters.getTriggerCaptureConfig(testKey, testValue))
+ )
+
+ // Assert
+ val triggerConfig = sessionProcessor.assertStartTriggerInvoked()
+ assertThat(triggerConfig.isParameterSet(testKey, testValue)).isTrue()
+ sessionConfigParameters.assertTriggerCompleted()
+ }
+
private fun <T> Config.isParameterSet(key: CaptureRequest.Key<T>, objValue: T): Boolean {
val options = CaptureRequestOptions.Builder.from(this).build()
return Objects.equals(
@@ -396,41 +433,6 @@
}
@Test
- fun willCancelRequests_whenIssueMultipleConfigs(): Unit = runBlocking(Dispatchers.Main) {
- // Arrange
- val cameraDevice = cameraDeviceHolder.get()!!
- val captureSession = createProcessingCaptureSession()
- captureSession.open(
- sessionConfigParameters.getSessionConfigForOpen(), cameraDevice,
- captureSessionOpenerBuilder.build()
- ).awaitWithTimeout(3000)
-
- val cancelCountLatch = CountDownLatch(2)
- val captureConfig1 = CaptureConfig.Builder().apply {
- templateType = CameraDevice.TEMPLATE_STILL_CAPTURE
- addCameraCaptureCallback(object : CameraCaptureCallback() {
- override fun onCaptureCancelled() {
- cancelCountLatch.countDown()
- }
- })
- }.build()
- val captureConfig2 = CaptureConfig.Builder().apply {
- templateType = CameraDevice.TEMPLATE_STILL_CAPTURE
- addCameraCaptureCallback(object : CameraCaptureCallback() {
- override fun onCaptureCancelled() {
- cancelCountLatch.countDown()
- }
- })
- }.build()
-
- // Act
- captureSession.issueCaptureRequests(listOf(captureConfig1, captureConfig2))
-
- // Assert
- assertThat(cancelCountLatch.await(3, TimeUnit.SECONDS)).isTrue()
- }
-
- @Test
fun willCancelNonStillCaptureRequests(): Unit = runBlocking(Dispatchers.Main) {
// Arrange
val cameraDevice = cameraDeviceHolder.get()!!
@@ -457,37 +459,6 @@
}
@Test
- fun willCancelRequests_whenPendingRequestNotFinished(): Unit = runBlocking(Dispatchers.Main) {
- // Arrange
- val cameraDevice = cameraDeviceHolder.get()!!
- val captureSession = createProcessingCaptureSession()
- captureSession.open(
- sessionConfigParameters.getSessionConfigForOpen(), cameraDevice,
- captureSessionOpenerBuilder.build()
- ).awaitWithTimeout(3000)
-
- // Act
- captureSession.issueCaptureRequests(
- listOf(sessionConfigParameters.getStillCaptureCaptureConfig())
- )
-
- val cancelCountLatch = CountDownLatch(1)
- val captureConfig = CaptureConfig.Builder().apply {
- templateType = CameraDevice.TEMPLATE_STILL_CAPTURE
- addCameraCaptureCallback(object : CameraCaptureCallback() {
- override fun onCaptureCancelled() {
- cancelCountLatch.countDown()
- }
- })
- }.build()
- // send 2nd request immediately.
- captureSession.issueCaptureRequests(listOf(captureConfig))
-
- // Assert
- assertThat(cancelCountLatch.await(3, TimeUnit.SECONDS)).isTrue()
- }
-
- @Test
fun canExecuteStillCaptureOneByOne(): Unit = runBlocking(Dispatchers.Main) {
// Arrange
val cameraDevice = cameraDeviceHolder.get()!!
@@ -761,6 +732,7 @@
private val previewImageReady = CompletableDeferred<Unit>()
private val captureImageReady = CompletableDeferred<Unit>()
private val stillCaptureCompleted = CompletableDeferred<Unit>()
+ private val triggerRequestCompleted = CompletableDeferred<Unit>()
private val tagKey1 = "KEY1"
private val tagKey2 = "KEY2"
private val tagValue1 = "Value1"
@@ -890,6 +862,23 @@
}.build()
}
+ fun <T : Any> getTriggerCaptureConfig(
+ triggerKey: CaptureRequest.Key<T>,
+ triggerValue: T
+ ): CaptureConfig {
+ return CaptureConfig.Builder().apply {
+ templateType = CameraDevice.TEMPLATE_PREVIEW
+ implementationOptions = CaptureRequestOptions.Builder().apply {
+ setCaptureRequestOption(triggerKey, triggerValue)
+ }.build()
+ addCameraCaptureCallback(object : CameraCaptureCallback() {
+ override fun onCaptureCompleted(cameraCaptureResult: CameraCaptureResult) {
+ triggerRequestCompleted.complete(Unit)
+ }
+ })
+ }.build()
+ }
+
fun closeOutputSurfaces() {
previewOutputDeferrableSurface.close()
captureOutputDeferrableSurface.close()
@@ -921,6 +910,10 @@
captureImageReady.awaitWithTimeout(3000)
}
+ suspend fun assertTriggerCompleted() {
+ triggerRequestCompleted.awaitWithTimeout(3000)
+ }
+
fun tearDown() {
closeOutputSurfaces()
}
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/ProcessingCaptureSession.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/ProcessingCaptureSession.java
index 173093a..e530e40 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/ProcessingCaptureSession.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/ProcessingCaptureSession.java
@@ -100,7 +100,7 @@
private ProcessorState mProcessorState;
private static List<DeferrableSurface> sHeldProcessorSurfaces = new ArrayList<>();
@Nullable
- private volatile CaptureConfig mPendingCaptureConfig = null;
+ private volatile List<CaptureConfig> mPendingCaptureConfigs = null;
@SuppressWarnings("WeakerAccess") /* synthetic accessor */
volatile boolean mIsExecutingStillCaptureRequest = false;
private final SessionProcessorCaptureCallback mSessionProcessorCaptureCallback;
@@ -280,33 +280,69 @@
}
}
- private boolean isStillCapture(@NonNull List<CaptureConfig> captureConfigs) {
- if (captureConfigs.isEmpty()) {
- return false;
- }
- for (CaptureConfig captureConfig : captureConfigs) {
- // Don't need to consider TEMPLATE_VIDEO_SNAPSHOT case since extensions does not
- // support Video Capture yet
- if (captureConfig.getTemplateType() != CameraDevice.TEMPLATE_STILL_CAPTURE) {
- return false;
+ /**
+ * Send a trigger request. Currently only CONTROL_AF_TRIGGER and CONTROL_AE_PRECAPTURE_TRIGGER
+ * are supported.
+ */
+ void issueTriggerRequest(@NonNull CaptureConfig captureConfig) {
+ Logger.d(TAG, "issueTriggerRequest");
+ CaptureRequestOptions options =
+ CaptureRequestOptions.Builder.from(
+ captureConfig.getImplementationOptions()).build();
+
+ boolean hasTriggerParameters = false;
+ for (Config.Option<?> option : options.listOptions()) {
+ @SuppressWarnings("unchecked")
+ CaptureRequest.Key<Object> key = (CaptureRequest.Key<Object>) option.getToken();
+ if (key.equals(CaptureRequest.CONTROL_AF_TRIGGER)
+ || key.equals(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER)) {
+ hasTriggerParameters = true;
+ break;
}
}
- return true;
+
+ if (!hasTriggerParameters) {
+ cancelRequests(Arrays.asList(captureConfig));
+ return;
+ }
+ mSessionProcessor.startTrigger(options, new SessionProcessor.CaptureCallback() {
+ @Override
+ public void onCaptureFailed(int captureSequenceId) {
+ mExecutor.execute(() -> {
+ for (CameraCaptureCallback cameraCaptureCallback :
+ captureConfig.getCameraCaptureCallbacks()) {
+ cameraCaptureCallback.onCaptureFailed(new CameraCaptureFailure(
+ CameraCaptureFailure.Reason.ERROR));
+ }
+ });
+ }
+
+ @Override
+ public void onCaptureSequenceCompleted(int captureSequenceId) {
+ mExecutor.execute(() -> {
+ for (CameraCaptureCallback cameraCaptureCallback :
+ captureConfig.getCameraCaptureCallbacks()) {
+ cameraCaptureCallback.onCaptureCompleted(
+ new CameraCaptureResult.EmptyCameraCaptureResult());
+ }
+ });
+ }
+ });
}
/**
- * Submit a still capture request via
- * {@link SessionProcessor#startCapture(SessionProcessor.CaptureCallback)}.
+ * Submit a list of capture requests.
*
- * <p>The method is more restrictive than {@link CaptureSession#issueCaptureRequests(List)}.
- * Only one @link CaptureConfig} with {@link CameraDevice#TEMPLATE_STILL_CAPTURE} template is
- * allowed. If the captureConfigs contain multiple {@link CaptureConfig}s or the contained
- * {@link CaptureConfig} does not use {@link CameraDevice#TEMPLATE_STILL_CAPTURE}, all
- * captureConfigs will be cancelled immediately.
+ * <p>Capture requests using {@link CameraDevice#TEMPLATE_STILL_CAPTURE} are executed by.
+ * {@link SessionProcessor#startCapture(SessionProcessor.CaptureCallback)}. Other
+ * capture requests that trigger {@link CaptureRequest#CONTROL_AF_TRIGGER} or
+ * {@link CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER} are executed by
+ * {@link SessionProcessor#startTrigger(Config, SessionProcessor.CaptureCallback)}.
*
- * <p>Camera2 capture options in {@link CaptureConfig#getImplementationOptions()} will be
+ * <p>For still capture requests, Camera2 capture options in
+ * {@link CaptureConfig#getImplementationOptions()} will be
* merged with the options in {@link SessionConfig#getImplementationOptions()} set by
- * {@link #setSessionConfig(SessionConfig)}. The merged parameters set will be passed to
+ * {@link #setSessionConfig(SessionConfig)}. The merged parameters set is passed to
* {@link SessionProcessor#setParameters(Config)} but it is up to the implementation of the
* {@link SessionProcessor} to determine which options to apply.
*
@@ -314,103 +350,30 @@
* to invoke callbacks of {@link CaptureCallbackContainer} type due to lack of the access to
* the camera2 {@link android.hardware.camera2.CameraCaptureSession.CaptureCallback}.
*
- * <p>Still capture requests are expected to arrive one at a time sequentially by upper layer.
- * Capture requests will be cancelled if previous request have not finished.
+ * <p>Although it allows concurrent capture requests to be submitted, the session processor
+ * might not support more than one capture request to execute at the same time. The session
+ * processor could fail the request immediately if it can't run multiple requests.
*/
@Override
public void issueCaptureRequests(@NonNull List<CaptureConfig> captureConfigs) {
if (captureConfigs.isEmpty()) {
return;
}
- if (captureConfigs.size() > 1 || !isStillCapture(captureConfigs)) {
- cancelRequests(captureConfigs);
- return;
- }
- // Only allows one capture config at a time.
- if (mPendingCaptureConfig != null || mIsExecutingStillCaptureRequest) {
- cancelRequests(captureConfigs);
- return;
- }
-
- // captureConfigs should contain exactly one CaptureConfig.
- CaptureConfig captureConfig = captureConfigs.get(0);
Logger.d(TAG, "issueCaptureRequests (id=" + mInstanceId + ") + state =" + mProcessorState);
-
switch (mProcessorState) {
case UNINITIALIZED:
case SESSION_INITIALIZED:
- mPendingCaptureConfig = captureConfig;
-
+ mPendingCaptureConfigs = captureConfigs;
break;
case ON_CAPTURE_SESSION_STARTED:
- mIsExecutingStillCaptureRequest = true;
- CaptureRequestOptions.Builder builder =
- CaptureRequestOptions.Builder.from(
- captureConfig.getImplementationOptions());
-
- if (captureConfig.getImplementationOptions().containsOption(
- CaptureConfig.OPTION_ROTATION)) {
- builder.setCaptureRequestOption(CaptureRequest.JPEG_ORIENTATION,
- captureConfig.getImplementationOptions().retrieveOption(
- CaptureConfig.OPTION_ROTATION));
+ for (CaptureConfig captureConfig : captureConfigs) {
+ if (captureConfig.getTemplateType() == CameraDevice.TEMPLATE_STILL_CAPTURE) {
+ issueStillCaptureRequest(captureConfig);
+ } else {
+ issueTriggerRequest(captureConfig);
+ }
}
-
- if (captureConfig.getImplementationOptions().containsOption(
- CaptureConfig.OPTION_JPEG_QUALITY)) {
- builder.setCaptureRequestOption(CaptureRequest.JPEG_QUALITY,
- captureConfig.getImplementationOptions().retrieveOption(
- CaptureConfig.OPTION_JPEG_QUALITY).byteValue());
- }
-
- mStillCaptureOptions = builder.build();
- updateParameters(mSessionOptions, mStillCaptureOptions);
- mSessionProcessor.startCapture(new SessionProcessor.CaptureCallback() {
- @Override
- public void onCaptureStarted(
- int captureSequenceId, long timestamp) {
- }
-
- @Override
- public void onCaptureProcessStarted(
- int captureSequenceId) {
- }
-
- @Override
- public void onCaptureFailed(
- int captureSequenceId) {
- mExecutor.execute(() -> {
- for (CameraCaptureCallback cameraCaptureCallback :
- captureConfig.getCameraCaptureCallbacks()) {
- cameraCaptureCallback.onCaptureFailed(new CameraCaptureFailure(
- CameraCaptureFailure.Reason.ERROR));
- }
- mIsExecutingStillCaptureRequest = false;
- });
- }
-
- @Override
- public void onCaptureSequenceCompleted(int captureSequenceId) {
- mExecutor.execute(() -> {
- for (CameraCaptureCallback cameraCaptureCallback :
- captureConfig.getCameraCaptureCallbacks()) {
- cameraCaptureCallback.onCaptureCompleted(
- new CameraCaptureResult.EmptyCameraCaptureResult());
- }
- mIsExecutingStillCaptureRequest = false;
- });
- }
-
- @Override
- public void onCaptureSequenceAborted(int captureSequenceId) {
- }
-
- @Override
- public void onCaptureCompleted(long timestamp, int captureSequenceId,
- @NonNull Map<CaptureResult.Key, Object> result) {
-
- }
- });
break;
case ON_CAPTURE_SESSION_ENDED:
case CLOSED:
@@ -420,6 +383,52 @@
break;
}
}
+ void issueStillCaptureRequest(@NonNull CaptureConfig captureConfig) {
+ CaptureRequestOptions.Builder builder =
+ CaptureRequestOptions.Builder.from(
+ captureConfig.getImplementationOptions());
+
+ if (captureConfig.getImplementationOptions().containsOption(
+ CaptureConfig.OPTION_ROTATION)) {
+ builder.setCaptureRequestOption(CaptureRequest.JPEG_ORIENTATION,
+ captureConfig.getImplementationOptions().retrieveOption(
+ CaptureConfig.OPTION_ROTATION));
+ }
+
+ if (captureConfig.getImplementationOptions().containsOption(
+ CaptureConfig.OPTION_JPEG_QUALITY)) {
+ builder.setCaptureRequestOption(CaptureRequest.JPEG_QUALITY,
+ captureConfig.getImplementationOptions().retrieveOption(
+ CaptureConfig.OPTION_JPEG_QUALITY).byteValue());
+ }
+
+ mStillCaptureOptions = builder.build();
+ updateParameters(mSessionOptions, mStillCaptureOptions);
+ mSessionProcessor.startCapture(new SessionProcessor.CaptureCallback() {
+ @Override
+ public void onCaptureFailed(
+ int captureSequenceId) {
+ mExecutor.execute(() -> {
+ for (CameraCaptureCallback cameraCaptureCallback :
+ captureConfig.getCameraCaptureCallbacks()) {
+ cameraCaptureCallback.onCaptureFailed(new CameraCaptureFailure(
+ CameraCaptureFailure.Reason.ERROR));
+ }
+ });
+ }
+
+ @Override
+ public void onCaptureSequenceCompleted(int captureSequenceId) {
+ mExecutor.execute(() -> {
+ for (CameraCaptureCallback cameraCaptureCallback :
+ captureConfig.getCameraCaptureCallbacks()) {
+ cameraCaptureCallback.onCaptureCompleted(
+ new CameraCaptureResult.EmptyCameraCaptureResult());
+ }
+ });
+ }
+ });
+ }
/**
* {@inheritDoc}
@@ -457,10 +466,9 @@
setSessionConfig(mSessionConfig);
}
- if (mPendingCaptureConfig != null) {
- List<CaptureConfig> pendingCaptureConfigList = Arrays.asList(mPendingCaptureConfig);
- mPendingCaptureConfig = null;
- issueCaptureRequests(pendingCaptureConfigList);
+ if (mPendingCaptureConfigs != null) {
+ issueCaptureRequests(mPendingCaptureConfigs);
+ mPendingCaptureConfigs = null;
}
}
@@ -479,8 +487,7 @@
@NonNull
@Override
public List<CaptureConfig> getCaptureConfigs() {
- return mPendingCaptureConfig != null ? Arrays.asList(mPendingCaptureConfig)
- : Collections.emptyList();
+ return mPendingCaptureConfigs != null ? mPendingCaptureConfigs : Collections.emptyList();
}
/**
@@ -489,12 +496,14 @@
@Override
public void cancelIssuedCaptureRequests() {
Logger.d(TAG, "cancelIssuedCaptureRequests (id=" + mInstanceId + ")");
- if (mPendingCaptureConfig != null) {
- for (CameraCaptureCallback cameraCaptureCallback :
- mPendingCaptureConfig.getCameraCaptureCallbacks()) {
- cameraCaptureCallback.onCaptureCancelled();
+ if (mPendingCaptureConfigs != null) {
+ for (CaptureConfig captureConfig : mPendingCaptureConfigs) {
+ for (CameraCaptureCallback cameraCaptureCallback :
+ captureConfig.getCameraCaptureCallbacks()) {
+ cameraCaptureCallback.onCaptureCancelled();
+ }
}
- mPendingCaptureConfig = null;
+ mPendingCaptureConfigs = null;
}
}
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/impl/SessionProcessor.java b/camera/camera-core/src/main/java/androidx/camera/core/impl/SessionProcessor.java
index 9237485..f9858f5 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/impl/SessionProcessor.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/impl/SessionProcessor.java
@@ -120,6 +120,13 @@
void abortCapture(int captureSequenceId);
/**
+ * Sends trigger-type single request such as AF/AE triggers.
+ */
+ default int startTrigger(@NonNull Config config, @NonNull CaptureCallback callback) {
+ return -1;
+ }
+
+ /**
* Callback for {@link #startRepeating} and {@link #startCapture}.
*/
interface CaptureCallback {
diff --git a/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/sessionprocessor/BasicExtenderSessionProcessor.java b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/sessionprocessor/BasicExtenderSessionProcessor.java
index 9bb522f..74de26b 100644
--- a/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/sessionprocessor/BasicExtenderSessionProcessor.java
+++ b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/sessionprocessor/BasicExtenderSessionProcessor.java
@@ -231,6 +231,7 @@
CaptureRequest.Key<Object> key = (CaptureRequest.Key<Object>) option.getToken();
map.put(key, options.retrieveOption(option));
}
+ mParameters.clear();
mParameters.putAll(map);
applyRotationAndJpegQualityToProcessor();
}
@@ -551,4 +552,42 @@
public void abortCapture(int captureSequenceId) {
mRequestProcessor.abortCaptures();
}
+
+ @Override
+ public int startTrigger(@NonNull Config config, @NonNull CaptureCallback callback) {
+ Logger.d(TAG, "startTrigger");
+ int captureSequenceId = mNextCaptureSequenceId.getAndIncrement();
+ RequestBuilder builder = new RequestBuilder();
+ builder.addTargetOutputConfigIds(mPreviewOutputConfig.getId());
+ if (mAnalysisOutputConfig != null) {
+ builder.addTargetOutputConfigIds(mAnalysisOutputConfig.getId());
+ }
+ builder.setTemplateId(CameraDevice.TEMPLATE_PREVIEW);
+ applyParameters(builder);
+ applyPreviewStagesParameters(builder);
+
+ CaptureRequestOptions options =
+ CaptureRequestOptions.Builder.from(config).build();
+ for (Config.Option<?> option : options.listOptions()) {
+ @SuppressWarnings("unchecked")
+ CaptureRequest.Key<Object> key = (CaptureRequest.Key<Object>) option.getToken();
+ builder.setParameters(key, options.retrieveOption(option));
+ }
+
+ mRequestProcessor.submit(builder.build(), new RequestProcessor.Callback() {
+ @Override
+ public void onCaptureCompleted(@NonNull RequestProcessor.Request request,
+ @NonNull CameraCaptureResult captureResult) {
+ callback.onCaptureSequenceCompleted(captureSequenceId);
+ }
+
+ @Override
+ public void onCaptureFailed(@NonNull RequestProcessor.Request request,
+ @NonNull CameraCaptureFailure captureFailure) {
+ callback.onCaptureFailed(captureSequenceId);
+ }
+ });
+
+ return captureSequenceId;
+ }
}
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeSessionProcessor.kt b/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeSessionProcessor.kt
index 2f98d10..1e57e06 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeSessionProcessor.kt
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/fakes/FakeSessionProcessor.kt
@@ -71,6 +71,7 @@
private val startRepeatingCalled = CompletableDeferred<Long>()
private val startCaptureCalled = CompletableDeferred<Long>()
private val setParametersCalled = CompletableDeferred<Config>()
+ private val startTriggerCalled = CompletableDeferred<Config>()
private var latestParameters: Config = OptionsBundle.emptyBundle()
private var blockRunAfterInitSession: () -> Unit = {}
@@ -314,6 +315,12 @@
return FAKE_CAPTURE_SEQUENCE_ID
}
+ override fun startTrigger(config: Config, callback: SessionProcessor.CaptureCallback): Int {
+ startTriggerCalled.complete(config)
+ callback.onCaptureSequenceCompleted(FAKE_CAPTURE_SEQUENCE_ID)
+ return FAKE_CAPTURE_SEQUENCE_ID
+ }
+
override fun abortCapture(captureSequenceId: Int) {
}
@@ -355,6 +362,10 @@
return setParametersCalled.awaitWithTimeout(3000)
}
+ suspend fun assertStartTriggerInvoked(): Config {
+ return startTriggerCalled.awaitWithTimeout(3000)
+ }
+
private suspend fun <T> Deferred<T>.awaitWithTimeout(timeMillis: Long): T {
return withTimeout(timeMillis) {
await()