Merge changes I1bc6361c,If19a4597 into androidx-main am: 154887a5f2
Original change: https://android-review.googlesource.com/c/platform/frameworks/support/+/2574550
Change-Id: I6dd2e5baa7f7197fc42bcf45198f0484d3edd219
Signed-off-by: Automerger Merge Worker <[email protected]>
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/impl/ExtendedCameraConfigProviderStore.java b/camera/camera-core/src/main/java/androidx/camera/core/impl/ExtendedCameraConfigProviderStore.java
index 725d8af..c59fab0 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/impl/ExtendedCameraConfigProviderStore.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/impl/ExtendedCameraConfigProviderStore.java
@@ -66,4 +66,13 @@
}
return provider;
}
+
+ /**
+ * Clear all {@link CameraConfigProvider} instances.
+ */
+ public static void clear() {
+ synchronized (LOCK) {
+ CAMERA_CONFIG_PROVIDERS.clear();
+ }
+ }
}
diff --git a/camera/camera-extensions/api/current.txt b/camera/camera-extensions/api/current.txt
index 0db7e93..5c6e740 100644
--- a/camera/camera-extensions/api/current.txt
+++ b/camera/camera-extensions/api/current.txt
@@ -15,6 +15,7 @@
method public androidx.camera.core.CameraSelector getExtensionEnabledCameraSelector(androidx.camera.core.CameraSelector, int);
method public static com.google.common.util.concurrent.ListenableFuture<androidx.camera.extensions.ExtensionsManager!> getInstanceAsync(android.content.Context, androidx.camera.core.CameraProvider);
method public boolean isExtensionAvailable(androidx.camera.core.CameraSelector, int);
+ method public boolean isImageAnalysisSupported(androidx.camera.core.CameraSelector, int);
}
}
diff --git a/camera/camera-extensions/api/restricted_current.txt b/camera/camera-extensions/api/restricted_current.txt
index 0db7e93..5c6e740 100644
--- a/camera/camera-extensions/api/restricted_current.txt
+++ b/camera/camera-extensions/api/restricted_current.txt
@@ -15,6 +15,7 @@
method public androidx.camera.core.CameraSelector getExtensionEnabledCameraSelector(androidx.camera.core.CameraSelector, int);
method public static com.google.common.util.concurrent.ListenableFuture<androidx.camera.extensions.ExtensionsManager!> getInstanceAsync(android.content.Context, androidx.camera.core.CameraProvider);
method public boolean isExtensionAvailable(androidx.camera.core.CameraSelector, int);
+ method public boolean isImageAnalysisSupported(androidx.camera.core.CameraSelector, int);
}
}
diff --git a/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/ExtensionsManagerTest.kt b/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/ExtensionsManagerTest.kt
index 94f0466..9d75cee 100644
--- a/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/ExtensionsManagerTest.kt
+++ b/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/ExtensionsManagerTest.kt
@@ -17,6 +17,8 @@
package androidx.camera.extensions
import android.hardware.camera2.CameraCharacteristics
+import android.util.Range
+import android.util.Size
import androidx.annotation.NonNull
import androidx.camera.camera2.interop.Camera2CameraInfo
import androidx.camera.core.Camera
@@ -26,6 +28,7 @@
import androidx.camera.core.impl.CameraInfoInternal
import androidx.camera.core.impl.MutableStateObservable
import androidx.camera.extensions.internal.ExtensionVersion
+import androidx.camera.extensions.internal.VendorExtender
import androidx.camera.extensions.internal.Version
import androidx.camera.extensions.internal.VersionName
import androidx.camera.extensions.util.ExtensionsTestUtil
@@ -267,7 +270,42 @@
}
@Test
- fun getEstimatedCaptureLatencyRangeThrowsException_whenExtensionAvailabilityIsNotAvailable() {
+ fun getEstimatedCaptureLatencyRange_returnValueFromExtender() {
+ extensionsManager = ExtensionsManager.getInstanceAsync(
+ context,
+ cameraProvider,
+ )[10000, TimeUnit.MILLISECONDS]
+
+ assumeTrue(
+ extensionsManager.extensionsAvailability
+ == ExtensionsManager.ExtensionsAvailability.LIBRARY_AVAILABLE
+ )
+ val estimatedCaptureLatency = Range(100L, 1000L)
+
+ val fakeVendorExtender = object : VendorExtender {
+ override fun isExtensionAvailable(
+ cameraId: String,
+ characteristicsMap: MutableMap<String, CameraCharacteristics>
+ ): Boolean {
+ return true
+ }
+
+ override fun getEstimatedCaptureLatencyRange(size: Size?): Range<Long>? {
+ return estimatedCaptureLatency
+ }
+ }
+ extensionsManager.setVendorExtenderFactory {
+ fakeVendorExtender
+ }
+
+ assertThat(extensionsManager.getEstimatedCaptureLatencyRange(
+ baseCameraSelector,
+ extensionMode)
+ ).isEqualTo(estimatedCaptureLatency)
+ }
+
+ @Test
+ fun getEstimatedCaptureLatencyRangeReturnNull_whenExtensionAvailabilityIsNotAvailable() {
extensionsManager = ExtensionsManager.getInstanceAsync(
context,
cameraProvider,
@@ -279,12 +317,10 @@
!= ExtensionsManager.ExtensionsAvailability.LIBRARY_AVAILABLE
)
- assertThrows<IllegalArgumentException> {
- extensionsManager.getEstimatedCaptureLatencyRange(
+ assertThat(extensionsManager.getEstimatedCaptureLatencyRange(
baseCameraSelector,
- extensionMode
- )
- }
+ extensionMode)
+ ).isNull()
}
@Test
@@ -306,19 +342,19 @@
}
@Test
- fun getEstimatedCaptureLatencyRangeThrowsException_whenNoCameraCanBeFound() {
+ fun getEstimatedCaptureLatencyRangeReturnsNull_whenNoCameraCanBeFound() {
checkExtensionAvailabilityAndInit()
val emptyCameraSelector = CameraSelector.Builder()
.addCameraFilter { _ -> ArrayList<CameraInfo>() }
.build()
- assertThrows<IllegalArgumentException> {
+ assertThat(
extensionsManager.getEstimatedCaptureLatencyRange(
emptyCameraSelector,
extensionMode
)
- }
+ ).isNull()
}
@Test
@@ -424,6 +460,107 @@
}
}
+ @Test
+ fun isImageAnalysisSupportedReturnsFalse_whenHasNoAnalysisSizes() {
+ extensionsManager = ExtensionsManager.getInstanceAsync(
+ context,
+ cameraProvider,
+ )[10000, TimeUnit.MILLISECONDS]
+
+ val fakeVendorExtender = object : VendorExtender {
+ override fun isExtensionAvailable(
+ cameraId: String,
+ characteristicsMap: MutableMap<String, CameraCharacteristics>
+ ): Boolean {
+ return true
+ }
+
+ override fun getSupportedYuvAnalysisResolutions(): Array<Size> {
+ return emptyArray()
+ }
+ }
+ extensionsManager.setVendorExtenderFactory {
+ fakeVendorExtender
+ }
+
+ assumeTrue(
+ extensionsManager.extensionsAvailability
+ == ExtensionsManager.ExtensionsAvailability.LIBRARY_AVAILABLE
+ )
+
+ assertThat(extensionsManager.isImageAnalysisSupported(
+ baseCameraSelector,
+ extensionMode)
+ ).isFalse()
+ }
+
+ @Test
+ fun isImageAnalysisSupportedReturnsTrue_whenHasAnalysisSizes() {
+ extensionsManager = ExtensionsManager.getInstanceAsync(
+ context,
+ cameraProvider,
+ )[10000, TimeUnit.MILLISECONDS]
+
+ val fakeVendorExtender = object : VendorExtender {
+ override fun isExtensionAvailable(
+ cameraId: String,
+ characteristicsMap: MutableMap<String, CameraCharacteristics>
+ ): Boolean {
+ return true
+ }
+
+ override fun getSupportedYuvAnalysisResolutions(): Array<Size> {
+ return arrayOf(Size(1920, 1080))
+ }
+ }
+ extensionsManager.setVendorExtenderFactory {
+ fakeVendorExtender
+ }
+
+ assumeTrue(
+ extensionsManager.extensionsAvailability
+ == ExtensionsManager.ExtensionsAvailability.LIBRARY_AVAILABLE
+ )
+
+ assertThat(extensionsManager.isImageAnalysisSupported(
+ baseCameraSelector,
+ extensionMode)
+ ).isTrue()
+ }
+ @Test
+ fun isImageAnalysisSupportedIsFalse_whenExtensionAvailabilityIsNotAvailable() {
+ extensionsManager = ExtensionsManager.getInstanceAsync(
+ context,
+ cameraProvider,
+ VersionName("99.0.0")
+ )[10000, TimeUnit.MILLISECONDS]
+
+ assumeTrue(
+ extensionsManager.extensionsAvailability
+ != ExtensionsManager.ExtensionsAvailability.LIBRARY_AVAILABLE
+ )
+
+ assertThat(extensionsManager.isImageAnalysisSupported(
+ baseCameraSelector,
+ extensionMode)
+ ).isFalse()
+ }
+
+ @Test
+ fun isImageAnalysisSupportedIsFalse_whenNoCameraCanBeFound() {
+ checkExtensionAvailabilityAndInit()
+ val emptyCameraSelector = CameraSelector.Builder()
+ .addCameraFilter { _ -> ArrayList<CameraInfo>() }
+ .build()
+
+ assertThat(
+ extensionsManager.isImageAnalysisSupported(
+ emptyCameraSelector,
+ extensionMode
+ )
+ ).isFalse()
+ }
+
private fun checkExtensionAvailabilityAndInit(): CameraSelector {
extensionsManager = ExtensionsManager.getInstanceAsync(
context,
@@ -444,21 +581,13 @@
}
private fun isExtensionAvailableByCameraInfo(cameraInfo: CameraInfo): Boolean {
- val characteristics = Camera2CameraInfo.extractCameraCharacteristics(cameraInfo)
- val cameraId = (cameraInfo as CameraInfoInternal).cameraId
- val imageCaptureExtenderImpl =
- ExtensionsTestUtil.createImageCaptureExtenderImpl(
- extensionMode,
- cameraId,
- characteristics
- )
- val previewExtenderImpl =
- ExtensionsTestUtil.createPreviewExtenderImpl(extensionMode, cameraId, characteristics)
+ var vendorExtender = ExtensionsTestUtil.createVendorExtender(extensionMode)
+ vendorExtender.init(cameraInfo)
+ val camera2CameraInfo = Camera2CameraInfo.from(cameraInfo)
+ val cameraId = camera2CameraInfo.cameraId
- return imageCaptureExtenderImpl.isExtensionAvailable(
- cameraId,
- characteristics
- ) && previewExtenderImpl.isExtensionAvailable(cameraId, characteristics)
+ return vendorExtender.isExtensionAvailable(cameraId,
+ camera2CameraInfo.cameraCharacteristicsMap)
}
private fun createVideoCapture(): VideoCapture<TestVideoOutput> {
diff --git a/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/ImageAnalysisTest.kt b/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/ImageAnalysisTest.kt
new file mode 100644
index 0000000..5ca9ee9
--- /dev/null
+++ b/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/ImageAnalysisTest.kt
@@ -0,0 +1,275 @@
+/*
+ * Copyright 2023 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.extensions
+
+import android.content.Context
+import android.graphics.ImageFormat
+import android.hardware.camera2.CameraCharacteristics
+import android.util.Pair
+import android.util.Size
+import androidx.camera.camera2.Camera2Config
+import androidx.camera.camera2.interop.Camera2CameraInfo
+import androidx.camera.core.Camera
+import androidx.camera.core.CameraSelector
+import androidx.camera.core.ImageAnalysis
+import androidx.camera.core.ImageCapture
+import androidx.camera.core.Preview
+import androidx.camera.core.impl.ImageFormatConstants
+import androidx.camera.core.impl.utils.executor.CameraXExecutors
+import androidx.camera.extensions.internal.VendorExtender
+import androidx.camera.extensions.util.ExtensionsTestUtil
+import androidx.camera.lifecycle.ProcessCameraProvider
+import androidx.camera.testing.CameraUtil
+import androidx.camera.testing.SurfaceTextureProvider
+import androidx.camera.testing.fakes.FakeLifecycleOwner
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.filters.LargeTest
+import androidx.test.filters.SdkSuppress
+import androidx.testutils.assertThrows
+import com.google.common.truth.Truth.assertThat
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.TimeUnit
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.withContext
+import org.junit.After
+import org.junit.Assume
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+@LargeTest
+@RunWith(Parameterized::class)
+@SdkSuppress(minSdkVersion = 21)
+class ImageAnalysisTest(
+ @ExtensionMode.Mode private val extensionMode: Int,
+ @CameraSelector.LensFacing private val lensFacing: Int
+) {
+ companion object {
+ @JvmStatic
+ @get:Parameterized.Parameters(name = "extension = {0}, facing = {1}")
+ val parameters: Collection<Array<Any>>
+ get() = ExtensionsTestUtil.getAllExtensionsLensFacingCombinations()
+ }
+
+ @get:Rule
+ val useCamera = CameraUtil.grantCameraPermissionAndPreTest(
+ CameraUtil.PreTestCameraIdList(Camera2Config.defaultConfig())
+ )
+
+ private val context = ApplicationProvider.getApplicationContext<Context>()
+ private lateinit var cameraProvider: ProcessCameraProvider
+ private lateinit var extensionsManager: ExtensionsManager
+ private lateinit var baseCameraSelector: CameraSelector
+ private lateinit var extensionsCameraSelector: CameraSelector
+ private lateinit var fakeLifecycleOwner: FakeLifecycleOwner
+ private lateinit var camera: Camera
+
+ @Before
+ fun setUp(): Unit = runBlocking {
+ Assume.assumeTrue(
+ ExtensionsTestUtil.isTargetDeviceAvailableForExtensions(
+ lensFacing,
+ extensionMode
+ )
+ )
+
+ cameraProvider = ProcessCameraProvider.getInstance(context)[10000, TimeUnit.MILLISECONDS]
+ baseCameraSelector = CameraSelector.Builder().requireLensFacing(lensFacing).build()
+ extensionsManager = ExtensionsManager.getInstanceAsync(
+ context,
+ cameraProvider
+ )[10000, TimeUnit.MILLISECONDS]
+
+ Assume.assumeTrue(extensionsManager.isExtensionAvailable(baseCameraSelector, extensionMode))
+
+ withContext(Dispatchers.Main) {
+ fakeLifecycleOwner = FakeLifecycleOwner().apply { startAndResume() }
+ camera = withContext(Dispatchers.Main) {
+ cameraProvider.bindToLifecycle(fakeLifecycleOwner, baseCameraSelector)
+ }
+ }
+ }
+
+ @After
+ fun teardown(): Unit = runBlocking {
+ if (::cameraProvider.isInitialized) {
+ cameraProvider.shutdown()[10000, TimeUnit.MILLISECONDS]
+ }
+
+ if (::extensionsManager.isInitialized) {
+ extensionsManager.shutdown()[10000, TimeUnit.MILLISECONDS]
+ }
+ }
+
+ @Test
+ fun canBindImageAnalysis_ifIsImageAnalysisSupportedReturnsTrue(): Unit = runBlocking {
+ // 1. Arrange
+ extensionsCameraSelector = extensionsManager.getExtensionEnabledCameraSelector(
+ baseCameraSelector,
+ extensionMode
+ )
+ Assume.assumeTrue(extensionsManager
+ .isImageAnalysisSupported(extensionsCameraSelector, extensionMode))
+
+ val analysisLatch = CountDownLatch(2)
+ withContext(Dispatchers.Main) {
+ val preview = Preview.Builder().build()
+ val imageCapture = ImageCapture.Builder().build()
+ val imageAnalysis = ImageAnalysis.Builder().build()
+
+ preview.setSurfaceProvider(
+ SurfaceTextureProvider.createSurfaceTextureProvider()
+ )
+
+ imageAnalysis.setAnalyzer(CameraXExecutors.ioExecutor()) {
+ analysisLatch.countDown()
+ it.close()
+ }
+
+ // 2. Act
+ cameraProvider.bindToLifecycle(
+ fakeLifecycleOwner,
+ extensionsCameraSelector,
+ preview, imageCapture, imageAnalysis
+ )
+ }
+
+ // 3. Assert
+ assertThat(analysisLatch.await(10000, TimeUnit.MILLISECONDS)).isTrue()
+ }
+
+ private fun getOutputSizes(imageFormat: Int): Array<Size> {
+ val map = Camera2CameraInfo.from(camera.cameraInfo)
+ .getCameraCharacteristic(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)!!
+ return map.getOutputSizes(imageFormat)
+ }
+
+ @Test
+ fun imageAnalysisResolutionIsFromVendorExtender(): Unit = runBlocking {
+ // 1. Arrange
+ val injectAnalysisSize = getOutputSizes(ImageFormat.YUV_420_888)
+ .minBy { it.width * it.height }
+ // Inject a fake VendorExtender that reports empty supported size for imageAnalysis.
+ extensionsManager.setVendorExtenderFactory {
+ object : VendorExtender {
+ override fun isExtensionAvailable(
+ cameraId: String,
+ characteristicsMap: MutableMap<String, CameraCharacteristics>
+ ) = true
+
+ override fun getSupportedYuvAnalysisResolutions(): Array<Size> {
+ return arrayOf(injectAnalysisSize)
+ }
+
+ override fun getSupportedPreviewOutputResolutions(): List<Pair<Int, Array<Size>>> {
+ return listOf(
+ Pair(ImageFormatConstants.INTERNAL_DEFINED_IMAGE_FORMAT_PRIVATE,
+ getOutputSizes(
+ ImageFormatConstants.INTERNAL_DEFINED_IMAGE_FORMAT_PRIVATE))
+ )
+ }
+
+ override fun getSupportedCaptureOutputResolutions(): List<Pair<Int, Array<Size>>> {
+ return listOf(
+ Pair(ImageFormat.JPEG,
+ getOutputSizes(ImageFormat.JPEG))
+ )
+ }
+ }
+ }
+
+ extensionsCameraSelector = extensionsManager.getExtensionEnabledCameraSelector(
+ baseCameraSelector,
+ extensionMode
+ )
+ assertThat(extensionsManager
+ .isImageAnalysisSupported(baseCameraSelector, extensionMode)).isTrue()
+ withContext(Dispatchers.Main) {
+ val preview = Preview.Builder().build()
+ val imageCapture = ImageCapture.Builder().build()
+ val imageAnalysis = ImageAnalysis.Builder().build()
+
+ // 2. Act
+ cameraProvider.bindToLifecycle(
+ fakeLifecycleOwner,
+ extensionsCameraSelector,
+ preview, imageCapture, imageAnalysis
+ )
+
+ // 3. Assert
+ assertThat(imageAnalysis.resolutionInfo!!.resolution).isEqualTo(injectAnalysisSize)
+ }
+ }
+
+ @Test
+ fun bindImageAnalysisThrowException_ifIsImageAnalysisSupportedReturnsFalse():
+ Unit = runBlocking {
+ // 1. Arrange
+ // Inject a fake VendorExtender that reports empty supported size for imageAnalysis.
+ extensionsManager.setVendorExtenderFactory {
+ object : VendorExtender {
+ override fun isExtensionAvailable(
+ cameraId: String,
+ characteristicsMap: MutableMap<String, CameraCharacteristics>
+ ) = true
+
+ override fun getSupportedYuvAnalysisResolutions(): Array<Size> {
+ return emptyArray()
+ }
+
+ override fun getSupportedPreviewOutputResolutions(): List<Pair<Int, Array<Size>>> {
+ return listOf(
+ Pair(ImageFormatConstants.INTERNAL_DEFINED_IMAGE_FORMAT_PRIVATE,
+ getOutputSizes(
+ ImageFormatConstants.INTERNAL_DEFINED_IMAGE_FORMAT_PRIVATE))
+ )
+ }
+
+ override fun getSupportedCaptureOutputResolutions(): List<Pair<Int, Array<Size>>> {
+ return listOf(
+ Pair(ImageFormat.JPEG,
+ getOutputSizes(ImageFormat.JPEG))
+ )
+ }
+ }
+ }
+
+ extensionsCameraSelector = extensionsManager.getExtensionEnabledCameraSelector(
+ baseCameraSelector,
+ extensionMode
+ )
+ assertThat(extensionsManager
+ .isImageAnalysisSupported(baseCameraSelector, extensionMode)).isFalse()
+ withContext(Dispatchers.Main) {
+ val preview = Preview.Builder().build()
+ val imageCapture = ImageCapture.Builder().build()
+ val imageAnalysis = ImageAnalysis.Builder().build()
+
+ // 3. Act && Assert
+ assertThrows<IllegalArgumentException> {
+ cameraProvider.bindToLifecycle(
+ fakeLifecycleOwner,
+ extensionsCameraSelector,
+ preview, imageCapture, imageAnalysis
+ )
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/util/ExtensionsTestUtil.java b/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/util/ExtensionsTestUtil.java
index 676b46a..0f50e2e 100644
--- a/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/util/ExtensionsTestUtil.java
+++ b/camera/camera-extensions/src/androidTest/java/androidx/camera/extensions/util/ExtensionsTestUtil.java
@@ -22,8 +22,6 @@
import static androidx.camera.extensions.ExtensionMode.HDR;
import static androidx.camera.extensions.ExtensionMode.NIGHT;
-import static junit.framework.TestCase.assertNotNull;
-
import android.hardware.camera2.CameraCharacteristics;
import android.os.Build;
@@ -31,18 +29,11 @@
import androidx.annotation.RequiresApi;
import androidx.camera.core.CameraSelector;
import androidx.camera.extensions.ExtensionMode;
-import androidx.camera.extensions.impl.AutoImageCaptureExtenderImpl;
-import androidx.camera.extensions.impl.AutoPreviewExtenderImpl;
-import androidx.camera.extensions.impl.BeautyImageCaptureExtenderImpl;
-import androidx.camera.extensions.impl.BeautyPreviewExtenderImpl;
-import androidx.camera.extensions.impl.BokehImageCaptureExtenderImpl;
-import androidx.camera.extensions.impl.BokehPreviewExtenderImpl;
-import androidx.camera.extensions.impl.HdrImageCaptureExtenderImpl;
-import androidx.camera.extensions.impl.HdrPreviewExtenderImpl;
-import androidx.camera.extensions.impl.ImageCaptureExtenderImpl;
-import androidx.camera.extensions.impl.NightImageCaptureExtenderImpl;
-import androidx.camera.extensions.impl.NightPreviewExtenderImpl;
-import androidx.camera.extensions.impl.PreviewExtenderImpl;
+import androidx.camera.extensions.internal.AdvancedVendorExtender;
+import androidx.camera.extensions.internal.BasicVendorExtender;
+import androidx.camera.extensions.internal.ExtensionVersion;
+import androidx.camera.extensions.internal.VendorExtender;
+import androidx.camera.extensions.internal.Version;
import androidx.camera.extensions.internal.compat.workaround.ExtensionDisabledValidator;
import androidx.camera.testing.CameraUtil;
@@ -71,84 +62,6 @@
}
/**
- * Creates an {@link ImageCaptureExtenderImpl} object for specific {@link ExtensionMode} and
- * camera id.
- *
- * @param extensionMode The extension mode for the created object.
- * @param cameraId The target camera id.
- * @param cameraCharacteristics The camera characteristics of the target camera.
- * @return An {@link ImageCaptureExtenderImpl} object.
- */
- @NonNull
- public static ImageCaptureExtenderImpl createImageCaptureExtenderImpl(
- @ExtensionMode.Mode int extensionMode, @NonNull String cameraId,
- @NonNull CameraCharacteristics cameraCharacteristics) {
- ImageCaptureExtenderImpl impl = null;
-
- switch (extensionMode) {
- case HDR:
- impl = new HdrImageCaptureExtenderImpl();
- break;
- case BOKEH:
- impl = new BokehImageCaptureExtenderImpl();
- break;
- case FACE_RETOUCH:
- impl = new BeautyImageCaptureExtenderImpl();
- break;
- case NIGHT:
- impl = new NightImageCaptureExtenderImpl();
- break;
- case AUTO:
- impl = new AutoImageCaptureExtenderImpl();
- break;
- }
- assertNotNull(impl);
-
- impl.init(cameraId, cameraCharacteristics);
-
- return impl;
- }
-
- /**
- * Creates a {@link PreviewExtenderImpl} object for specific {@link ExtensionMode} and
- * camera id.
- *
- * @param extensionMode The extension mode for the created object.
- * @param cameraId The target camera id.
- * @param cameraCharacteristics The camera characteristics of the target camera.
- * @return A {@link PreviewExtenderImpl} object.
- */
- @NonNull
- public static PreviewExtenderImpl createPreviewExtenderImpl(
- @ExtensionMode.Mode int extensionMode, @NonNull String cameraId,
- @NonNull CameraCharacteristics cameraCharacteristics) {
- PreviewExtenderImpl impl = null;
-
- switch (extensionMode) {
- case HDR:
- impl = new HdrPreviewExtenderImpl();
- break;
- case BOKEH:
- impl = new BokehPreviewExtenderImpl();
- break;
- case FACE_RETOUCH:
- impl = new BeautyPreviewExtenderImpl();
- break;
- case NIGHT:
- impl = new NightPreviewExtenderImpl();
- break;
- case AUTO:
- impl = new AutoPreviewExtenderImpl();
- break;
- }
- assertNotNull(impl);
-
- impl.init(cameraId, cameraCharacteristics);
-
- return impl;
- }
-
- /**
* Returns whether the target camera device can support the test for a specific extension mode.
*/
public static boolean isTargetDeviceAvailableForExtensions(
@@ -157,6 +70,20 @@
&& !isSpecificSkippedDevice() && !isSpecificSkippedDeviceWithExtensionMode(mode);
}
+ private static boolean isAdvancedExtenderSupported() {
+ if (ExtensionVersion.getRuntimeVersion().compareTo(Version.VERSION_1_2) < 0) {
+ return false;
+ }
+ return ExtensionVersion.isAdvancedExtenderSupported();
+ }
+
+ public static VendorExtender createVendorExtender(@ExtensionMode.Mode int mode) {
+ if (isAdvancedExtenderSupported()) {
+ return new AdvancedVendorExtender(mode);
+ }
+ return new BasicVendorExtender(mode);
+ }
+
/**
* Returns whether the device is LIMITED hardware level above.
*
diff --git a/camera/camera-extensions/src/main/java/androidx/camera/extensions/ExtensionsInfo.java b/camera/camera-extensions/src/main/java/androidx/camera/extensions/ExtensionsInfo.java
index 912592b0..6ef8e616 100644
--- a/camera/camera-extensions/src/main/java/androidx/camera/extensions/ExtensionsInfo.java
+++ b/camera/camera-extensions/src/main/java/androidx/camera/extensions/ExtensionsInfo.java
@@ -25,6 +25,7 @@
import androidx.annotation.Nullable;
import androidx.annotation.OptIn;
import androidx.annotation.RequiresApi;
+import androidx.annotation.VisibleForTesting;
import androidx.camera.camera2.interop.ExperimentalCamera2Interop;
import androidx.camera.core.CameraFilter;
import androidx.camera.core.CameraInfo;
@@ -60,9 +61,12 @@
private static final String EXTENDED_CAMERA_CONFIG_PROVIDER_ID_PREFIX = ":camera:camera"
+ "-extensions-";
private final CameraProvider mCameraProvider;
+ @NonNull
+ private VendorExtenderFactory mVendorExtenderFactory;
ExtensionsInfo(@NonNull CameraProvider cameraProvider) {
mCameraProvider = cameraProvider;
+ mVendorExtenderFactory = (extensionMode) -> getVendorExtender(extensionMode);
}
/**
@@ -163,7 +167,8 @@
newCameraSelector.filter(mCameraProvider.getAvailableCameraInfos());
if (cameraInfos.isEmpty()) {
- throw new IllegalArgumentException("No cameras found for given CameraSelector");
+ // Returns null if the specified extension mode is not available.
+ return null;
}
extensionsCameraInfo = cameraInfos.get(0);
@@ -174,7 +179,7 @@
}
try {
- VendorExtender vendorExtender = getVendorExtender(mode);
+ VendorExtender vendorExtender = mVendorExtenderFactory.createVendorExtender(mode);
vendorExtender.init(extensionsCameraInfo);
return vendorExtender.getEstimatedCaptureLatencyRange(resolution);
@@ -183,11 +188,36 @@
}
}
- private static CameraFilter getFilter(@ExtensionMode.Mode int mode) {
+ boolean isImageAnalysisSupported(@NonNull CameraSelector cameraSelector,
+ @ExtensionMode.Mode int mode) {
+ CameraSelector newCameraSelector = CameraSelector.Builder.fromSelector(
+ cameraSelector).addCameraFilter(getFilter(mode)).build();
+ CameraInfo extensionsCameraInfo;
+ List<CameraInfo> cameraInfos =
+ newCameraSelector.filter(mCameraProvider.getAvailableCameraInfos());
+
+ if (cameraInfos.isEmpty()) {
+ // Returns false if the specified extension mode is not available on this camera.
+ return false;
+ }
+
+ extensionsCameraInfo = cameraInfos.get(0);
+ VendorExtender vendorExtender = mVendorExtenderFactory.createVendorExtender(mode);
+ vendorExtender.init(extensionsCameraInfo);
+ Size[] supportedYuvSizes = vendorExtender.getSupportedYuvAnalysisResolutions();
+ return supportedYuvSizes != null && supportedYuvSizes.length > 0;
+ }
+
+ @VisibleForTesting
+ void setVendorExtenderFactory(@NonNull VendorExtenderFactory factory) {
+ mVendorExtenderFactory = factory;
+ }
+
+ private CameraFilter getFilter(@ExtensionMode.Mode int mode) {
CameraFilter filter;
String id = getExtendedCameraConfigProviderId(mode);
- VendorExtender vendorExtender = getVendorExtender(mode);
+ VendorExtender vendorExtender = mVendorExtenderFactory.createVendorExtender(mode);
filter = new ExtensionCameraFilter(id, vendorExtender);
return filter;
}
@@ -196,12 +226,12 @@
* Injects {@link CameraConfigProvider} for specified extension mode to the
* {@link ExtendedCameraConfigProviderStore}.
*/
- private static void injectExtensionCameraConfig(@ExtensionMode.Mode int mode) {
+ private void injectExtensionCameraConfig(@ExtensionMode.Mode int mode) {
Identifier id = Identifier.create(getExtendedCameraConfigProviderId(mode));
if (ExtendedCameraConfigProviderStore.getConfigProvider(id) == CameraConfigProvider.EMPTY) {
ExtendedCameraConfigProviderStore.addConfig(id, (cameraInfo, context) -> {
- VendorExtender vendorExtender = getVendorExtender(mode);
+ VendorExtender vendorExtender = mVendorExtenderFactory.createVendorExtender(mode);
vendorExtender.init(cameraInfo);
ExtensionsUseCaseConfigFactory factory = new
@@ -226,7 +256,7 @@
}
@NonNull
- private static VendorExtender getVendorExtender(int mode) {
+ static VendorExtender getVendorExtender(@ExtensionMode.Mode int mode) {
boolean isAdvancedExtenderSupported = isAdvancedExtenderSupported();
VendorExtender vendorExtender;
diff --git a/camera/camera-extensions/src/main/java/androidx/camera/extensions/ExtensionsManager.java b/camera/camera-extensions/src/main/java/androidx/camera/extensions/ExtensionsManager.java
index b6c7ced..9020456 100644
--- a/camera/camera-extensions/src/main/java/androidx/camera/extensions/ExtensionsManager.java
+++ b/camera/camera-extensions/src/main/java/androidx/camera/extensions/ExtensionsManager.java
@@ -31,6 +31,7 @@
import androidx.camera.core.ImageCapture;
import androidx.camera.core.Logger;
import androidx.camera.core.Preview;
+import androidx.camera.core.impl.ExtendedCameraConfigProviderStore;
import androidx.camera.core.impl.utils.ContextUtil;
import androidx.camera.core.impl.utils.executor.CameraXExecutors;
import androidx.camera.core.impl.utils.futures.Futures;
@@ -312,6 +313,7 @@
// Once extension has been initialized start the deinit call
if (availability == ExtensionsAvailability.LIBRARY_AVAILABLE) {
+ ExtendedCameraConfigProviderStore.clear();
sDeinitializeFuture = CallbackToFutureAdapter.getFuture(completer -> {
try {
InitializerImpl.deinit(
@@ -423,29 +425,59 @@
* specified extension mode.
* @param mode The extension mode to check.
* @return the range of estimated minimal and maximal capture latency in milliseconds.
- * Returns null if no capture latency info can be provided.
- * @throws IllegalArgumentException If this device doesn't support extensions function, or no
- * camera can be found to support the specified extension mode.
+ * Returns null if no capture latency info can be provided or if the device doesn't support
+ * the extension mode on this camera.
*/
@Nullable
public Range<Long> getEstimatedCaptureLatencyRange(@NonNull CameraSelector cameraSelector,
@ExtensionMode.Mode int mode) {
if (mode == ExtensionMode.NONE
|| mExtensionsAvailability != ExtensionsAvailability.LIBRARY_AVAILABLE) {
- throw new IllegalArgumentException(
- "No camera can be found to support the specified extensions mode! "
- + "isExtensionAvailable should be checked first before calling "
- + "getEstimatedCaptureLatencyRange.");
+ // Returns null for non-Extensions mode or if Extensions are not supported on this
+ // device.
+ return null;
}
return mExtensionsInfo.getEstimatedCaptureLatencyRange(cameraSelector, mode, null);
}
+ /**
+ * Returns whether the given extension mode supports the {@link ImageAnalysis} use case on
+ * the camera specified by the given {@link CameraSelector}. If it returns false, invoking
+ * {@code ProcessCameraProvider.bindToLifecycle} with an {@link ImageAnalysis} use case will
+ * throw an {@link IllegalArgumentException}.
+ *
+ * @param cameraSelector The {@link CameraSelector} to find a camera which supports the
+ * specified extension mode.
+ * @param mode The extension mode to check.
+ * @return true if {@link ImageAnalysis} can be bound when the specified extension mode is
+ * enabled on the camera specified by the given {@link CameraSelector}. Returns false
+ * otherwise. If the device doesn't support this extension mode on this camera, it will also
+ * return false.
+ */
+ public boolean isImageAnalysisSupported(@NonNull CameraSelector cameraSelector,
+ @ExtensionMode.Mode int mode) {
+ if (mode == ExtensionMode.NONE) {
+ return true;
+ }
+
+ // Returns false if Extensions are not supported on this device.
+ if (mExtensionsAvailability != ExtensionsAvailability.LIBRARY_AVAILABLE) {
+ return false;
+ }
+
+ return mExtensionsInfo.isImageAnalysisSupported(cameraSelector, mode);
+ }
+
@VisibleForTesting
@NonNull
ExtensionsAvailability getExtensionsAvailability() {
return mExtensionsAvailability;
}
+ @VisibleForTesting
+ void setVendorExtenderFactory(VendorExtenderFactory vendorExtenderFactory) {
+ mExtensionsInfo.setVendorExtenderFactory(vendorExtenderFactory);
+ }
private ExtensionsManager(@NonNull ExtensionsAvailability extensionsAvailability,
@NonNull CameraProvider cameraProvider) {
diff --git a/camera/camera-extensions/src/main/java/androidx/camera/extensions/VendorExtenderFactory.java b/camera/camera-extensions/src/main/java/androidx/camera/extensions/VendorExtenderFactory.java
new file mode 100644
index 0000000..f8715c35
--- /dev/null
+++ b/camera/camera-extensions/src/main/java/androidx/camera/extensions/VendorExtenderFactory.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2023 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.extensions;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
+import androidx.annotation.RestrictTo;
+import androidx.camera.extensions.internal.VendorExtender;
+
+@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+interface VendorExtenderFactory {
+ @NonNull
+ VendorExtender createVendorExtender(@ExtensionMode.Mode int extensionMode);
+}
diff --git a/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/BasicVendorExtender.java b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/BasicVendorExtender.java
index 61f0550..d78bc97 100644
--- a/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/BasicVendorExtender.java
+++ b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/BasicVendorExtender.java
@@ -35,6 +35,7 @@
import androidx.camera.camera2.interop.ExperimentalCamera2Interop;
import androidx.camera.core.CameraInfo;
import androidx.camera.core.Logger;
+import androidx.camera.core.impl.ImageFormatConstants;
import androidx.camera.core.impl.SessionProcessor;
import androidx.camera.extensions.ExtensionMode;
import androidx.camera.extensions.impl.AutoImageCaptureExtenderImpl;
@@ -63,8 +64,7 @@
/**
* Basic vendor interface implementation
*/
-@RequiresApi(23) // TODO(b/200306659): Remove and replace with annotation on package-info.java
-// replaceImageFormatIfMissing accesses ImageFormat#PRIVATE which is public since API level 23.
+@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
public class BasicVendorExtender implements VendorExtender {
private static final String TAG = "BasicVendorExtender";
private final ExtensionDisabledValidator mExtensionDisabledValidator =
@@ -208,7 +208,7 @@
== PreviewExtenderImpl.ProcessorType.PROCESSOR_TYPE_IMAGE_PROCESSOR) {
return ImageFormat.YUV_420_888;
} else {
- return ImageFormat.PRIVATE;
+ return ImageFormatConstants.INTERNAL_DEFINED_IMAGE_FORMAT_PRIVATE /* PRIVATE */;
}
}
@@ -227,8 +227,10 @@
// PreviewExtenderImpl.getSupportedResolutions() returns the supported size
// for input surface. We need to ensure output surface format is supported.
return replaceImageFormatIfMissing(result,
- ImageFormat.YUV_420_888 /* formatToBeReplaced */,
- ImageFormat.PRIVATE /* newFormat */);
+ /* formatToBeReplaced */
+ ImageFormat.YUV_420_888,
+ /* newFormat */
+ ImageFormatConstants.INTERNAL_DEFINED_IMAGE_FORMAT_PRIVATE);
}
} catch (NoSuchMethodError e) {
}
@@ -241,7 +243,8 @@
// able to output to the output surface, therefore we fetch the sizes from the
// input image format for the output format.
int inputImageFormat = getPreviewInputImageFormat();
- return Arrays.asList(new Pair<>(ImageFormat.PRIVATE, getOutputSizes(inputImageFormat)));
+ return Arrays.asList(new Pair<>(ImageFormatConstants.INTERNAL_DEFINED_IMAGE_FORMAT_PRIVATE,
+ getOutputSizes(inputImageFormat)));
}
diff --git a/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/ExtensionsUseCaseConfigFactory.java b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/ExtensionsUseCaseConfigFactory.java
index 35f2a92..a3d601c 100644
--- a/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/ExtensionsUseCaseConfigFactory.java
+++ b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/ExtensionsUseCaseConfigFactory.java
@@ -18,16 +18,23 @@
import static androidx.camera.core.impl.UseCaseConfig.OPTION_ZSL_DISABLED;
+import android.graphics.ImageFormat;
+import android.util.Pair;
+import android.util.Size;
+
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.camera.core.ImageCapture.CaptureMode;
import androidx.camera.core.impl.Config;
+import androidx.camera.core.impl.ImageAnalysisConfig;
import androidx.camera.core.impl.MutableOptionsBundle;
import androidx.camera.core.impl.OptionsBundle;
import androidx.camera.core.impl.UseCaseConfigFactory;
import androidx.camera.extensions.ExtensionMode;
+import java.util.List;
+
/**
* Implementation of UseCaseConfigFactory to provide the default extensions configurations for use
* cases.
@@ -36,12 +43,33 @@
public final class ExtensionsUseCaseConfigFactory implements UseCaseConfigFactory {
private final ImageCaptureConfigProvider mImageCaptureConfigProvider;
private final PreviewConfigProvider mPreviewConfigProvider;
+ private final ImageAnalysisConfigProvider mImageAnalysisConfigProvider;
public ExtensionsUseCaseConfigFactory(
@ExtensionMode.Mode int mode,
@NonNull VendorExtender vendorExtender) {
mImageCaptureConfigProvider = new ImageCaptureConfigProvider(mode, vendorExtender);
mPreviewConfigProvider = new PreviewConfigProvider(mode, vendorExtender);
+ mImageAnalysisConfigProvider = new ImageAnalysisConfigProvider(vendorExtender);
+ }
+
+ private boolean isImageAnalysisSupported(
+ @Nullable List<Pair<Integer, Size[]>> supportedResolutions) {
+ if (supportedResolutions == null) {
+ return false;
+ }
+
+ for (Pair<Integer, Size[]> pair : supportedResolutions) {
+ int imageFormat = pair.first;
+ Size[] sizes = pair.second;
+ if (imageFormat == ImageFormat.YUV_420_888) {
+ if (sizes != null && sizes.length > 0) {
+ return true;
+ }
+ }
+ }
+
+ return false;
}
/**
@@ -65,6 +93,20 @@
mutableOptionsBundle =
MutableOptionsBundle.from(mPreviewConfigProvider.getConfig());
break;
+ case IMAGE_ANALYSIS: // invoked when ImageAnalysis is bound.
+ ImageAnalysisConfig config = mImageAnalysisConfigProvider.getConfig();
+ List<Pair<Integer, Size[]>> supportedResolutions =
+ config.getSupportedResolutions(/* valueIfMissing */ null);
+ if (!isImageAnalysisSupported(supportedResolutions)) {
+ // This will be thrown when invoking bindToLifecycle.
+ throw new IllegalArgumentException(
+ "ImageAnalysis is not supported when Extension is enabled on "
+ + "this device. Check "
+ + "ExtensionsManager.isImageAnalysisSupported before binding "
+ + "the ImageAnalysis use case.");
+ }
+ mutableOptionsBundle = MutableOptionsBundle.from(config);
+ break;
case VIDEO_CAPTURE:
throw new IllegalArgumentException("CameraX Extensions doesn't support "
+ "VideoCapture!");
diff --git a/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/ImageAnalysisConfigProvider.java b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/ImageAnalysisConfigProvider.java
new file mode 100644
index 0000000..e2d6a64
--- /dev/null
+++ b/camera/camera-extensions/src/main/java/androidx/camera/extensions/internal/ImageAnalysisConfigProvider.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2023 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.extensions.internal;
+
+import android.graphics.ImageFormat;
+import android.util.Pair;
+import android.util.Size;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
+import androidx.camera.core.ImageAnalysis;
+import androidx.camera.core.impl.ConfigProvider;
+import androidx.camera.core.impl.ImageAnalysisConfig;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Provides extensions related configs for image analysis
+ */
+@RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
+public class ImageAnalysisConfigProvider implements ConfigProvider<ImageAnalysisConfig> {
+ private final VendorExtender mVendorExtender;
+ public ImageAnalysisConfigProvider(
+ @NonNull VendorExtender vendorExtender) {
+ mVendorExtender = vendorExtender;
+ }
+
+ @NonNull
+ @Override
+ public ImageAnalysisConfig getConfig() {
+ ImageAnalysis.Builder builder = new ImageAnalysis.Builder();
+ Size[] sizes = mVendorExtender.getSupportedYuvAnalysisResolutions();
+ List<Pair<Integer, Size[]>> sizeList = new ArrayList<>();
+ if (sizes != null && sizes.length > 0) {
+ sizeList.add(new Pair<>(ImageFormat.YUV_420_888, sizes));
+ }
+ builder.setSupportedResolutions(sizeList);
+ return builder.getUseCaseConfig();
+ }
+}
diff --git a/camera/integration-tests/extensionstestapp/src/main/java/androidx/camera/integration/extensions/CameraExtensionsActivity.java b/camera/integration-tests/extensionstestapp/src/main/java/androidx/camera/integration/extensions/CameraExtensionsActivity.java
index 85d64c1..d41cdc5 100644
--- a/camera/integration-tests/extensionstestapp/src/main/java/androidx/camera/integration/extensions/CameraExtensionsActivity.java
+++ b/camera/integration-tests/extensionstestapp/src/main/java/androidx/camera/integration/extensions/CameraExtensionsActivity.java
@@ -65,10 +65,12 @@
import androidx.camera.core.CameraInfo;
import androidx.camera.core.CameraSelector;
import androidx.camera.core.FocusMeteringAction;
+import androidx.camera.core.ImageAnalysis;
import androidx.camera.core.ImageCapture;
import androidx.camera.core.ImageCaptureException;
import androidx.camera.core.MeteringPoint;
import androidx.camera.core.Preview;
+import androidx.camera.core.UseCaseGroup;
import androidx.camera.core.impl.utils.executor.CameraXExecutors;
import androidx.camera.extensions.ExtensionMode;
import androidx.camera.extensions.ExtensionsManager;
@@ -271,7 +273,23 @@
mCurrentCameraSelector, mCurrentExtensionMode);
mCameraProvider.unbindAll();
- mCamera = mCameraProvider.bindToLifecycle(this, cameraSelector, mImageCapture, mPreview);
+
+ UseCaseGroup.Builder useCaseGroupBuilder =
+ new UseCaseGroup.Builder()
+ .addUseCase(mPreview)
+ .addUseCase(mImageCapture);
+
+ if (mExtensionsManager.isImageAnalysisSupported(cameraSelector,
+ mCurrentExtensionMode)) {
+ ImageAnalysis imageAnalysis = new ImageAnalysis.Builder().build();
+ imageAnalysis.setAnalyzer(CameraXExecutors.ioExecutor(), img -> {
+ img.close();
+ });
+ useCaseGroupBuilder.addUseCase(imageAnalysis);
+ }
+
+ mCamera = mCameraProvider.bindToLifecycle(this, cameraSelector,
+ useCaseGroupBuilder.build());
// Update the UI and save location for ImageCapture
Button toggleButton = findViewById(R.id.PhotoToggle);