Merge "Fix SupportedQualitiesVerificationTest" into androidx-main
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseManager.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseManager.kt
index 7021369..0e99e30 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseManager.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseManager.kt
@@ -16,6 +16,8 @@
package androidx.camera.camera2.pipe.integration.impl
+import android.media.MediaCodec
+import android.os.Build
import androidx.annotation.GuardedBy
import androidx.annotation.RequiresApi
import androidx.camera.camera2.pipe.core.Log
@@ -26,8 +28,9 @@
import androidx.camera.camera2.pipe.integration.config.UseCaseCameraConfig
import androidx.camera.camera2.pipe.integration.interop.Camera2CameraControl
import androidx.camera.camera2.pipe.integration.interop.ExperimentalCamera2Interop
-import androidx.camera.core.ImageCapture
import androidx.camera.core.UseCase
+import androidx.camera.core.impl.DeferrableSurface
+import androidx.camera.core.impl.SessionConfig.ValidatingBuilder
import javax.inject.Inject
import kotlinx.coroutines.Job
import kotlinx.coroutines.joinAll
@@ -259,10 +262,14 @@
@GuardedBy("lock")
private fun shouldAddRepeatingUseCase(runningUseCases: Set<UseCase>): Boolean {
- return !attachedUseCases.contains(meteringRepeating) &&
- runningUseCases.only { it is ImageCapture }
- }
+ val meteringRepeatingEnabled = attachedUseCases.contains(meteringRepeating)
+ val coreLibraryUseCases = runningUseCases.filterNot { it is MeteringRepeating }
+ val onlyVideoCapture = coreLibraryUseCases.onlyVideoCapture()
+ val requireMeteringRepeating = coreLibraryUseCases.requireMeteringRepeating()
+
+ return !meteringRepeatingEnabled && (onlyVideoCapture || requireMeteringRepeating)
+ }
@GuardedBy("lock")
private fun addRepeatingUseCase() {
meteringRepeating.setupSession()
@@ -272,11 +279,13 @@
@GuardedBy("lock")
private fun shouldRemoveRepeatingUseCase(runningUseCases: Set<UseCase>): Boolean {
- val onlyMeteringRepeatingEnabled = runningUseCases.only { it is MeteringRepeating }
- val meteringRepeatingAndNonImageCaptureEnabled =
- runningUseCases.any { it is MeteringRepeating } &&
- runningUseCases.any { it !is MeteringRepeating && it !is ImageCapture }
- return onlyMeteringRepeatingEnabled || meteringRepeatingAndNonImageCaptureEnabled
+ val meteringRepeatingEnabled = runningUseCases.contains(meteringRepeating)
+
+ val coreLibraryUseCases = runningUseCases.filterNot { it is MeteringRepeating }
+ val onlyVideoCapture = coreLibraryUseCases.onlyVideoCapture()
+ val requireMeteringRepeating = coreLibraryUseCases.requireMeteringRepeating()
+
+ return meteringRepeatingEnabled && !onlyVideoCapture && !requireMeteringRepeating
}
@GuardedBy("lock")
@@ -286,11 +295,31 @@
meteringRepeating.onDetached()
}
- /**
- * Returns true when the collection only has elements (1 or more) that verify the predicate,
- * false otherwise.
- */
- private fun <T> Collection<T>.only(predicate: (T) -> Boolean): Boolean {
- return isNotEmpty() && all(predicate)
+ private fun Collection<UseCase>.onlyVideoCapture(): Boolean {
+ return isNotEmpty() && checkSurfaces { _, sessionSurfaces ->
+ sessionSurfaces.isNotEmpty() && sessionSurfaces.all {
+ it.containerClass == MediaCodec::class.java
+ }
+ }
+ }
+
+ private fun Collection<UseCase>.requireMeteringRepeating(): Boolean {
+ return isNotEmpty() && checkSurfaces { repeatingSurfaces, sessionSurfaces ->
+ // There is no repeating UseCases
+ sessionSurfaces.isNotEmpty() && repeatingSurfaces.isEmpty()
+ }
+ }
+
+ @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
+ private fun Collection<UseCase>.checkSurfaces(
+ predicate: (
+ repeatingSurfaces: List<DeferrableSurface>,
+ sessionSurfaces: List<DeferrableSurface>
+ ) -> Boolean
+ ): Boolean = ValidatingBuilder().let { validatingBuilder ->
+ forEach { useCase -> validatingBuilder.add(useCase.sessionConfig) }
+ val sessionConfig = validatingBuilder.build()
+ val captureConfig = sessionConfig.repeatingCaptureConfig
+ return predicate(captureConfig.surfaces, sessionConfig.surfaces)
}
}
diff --git a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/UseCaseManagerTest.kt b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/UseCaseManagerTest.kt
index a98af2c..5f4d5d2 100644
--- a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/UseCaseManagerTest.kt
+++ b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/UseCaseManagerTest.kt
@@ -17,6 +17,7 @@
package androidx.camera.camera2.pipe.integration.impl
import android.os.Build
+import android.util.Size
import androidx.camera.camera2.pipe.CameraId
import androidx.camera.camera2.pipe.integration.adapter.CameraStateAdapter
import androidx.camera.camera2.pipe.integration.adapter.RobolectricCameraPipeTestRunner
@@ -28,12 +29,18 @@
import androidx.camera.camera2.pipe.integration.testing.FakeUseCaseCameraComponentBuilder
import androidx.camera.core.ImageCapture
import androidx.camera.core.Preview
+import androidx.camera.core.UseCase
+import androidx.camera.core.impl.utils.executor.CameraXExecutors
+import androidx.camera.testing.SurfaceTextureProvider
+import androidx.camera.testing.fakes.FakeCamera
import androidx.test.core.app.ApplicationProvider
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.asExecutor
+import kotlinx.coroutines.runBlocking
+import org.junit.After
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.annotation.Config
@@ -41,6 +48,8 @@
@RunWith(RobolectricCameraPipeTestRunner::class)
@Config(minSdk = Build.VERSION_CODES.LOLLIPOP)
class UseCaseManagerTest {
+ private val useCaseManagerList = mutableListOf<UseCaseManager>()
+ private val useCaseList = mutableListOf<UseCase>()
private val useCaseThreads by lazy {
val dispatcher = Dispatchers.Default
val cameraScope = CoroutineScope(
@@ -55,11 +64,17 @@
)
}
+ @After
+ fun tearDown() = runBlocking {
+ useCaseManagerList.forEach { it.close() }
+ useCaseList.forEach { it.onDetached() }
+ }
+
@Test
fun enabledUseCasesEmpty_whenUseCaseAttachedOnly() {
// Arrange
val useCaseManager = createUseCaseManager()
- val useCase = Preview.Builder().build()
+ val useCase = createPreview()
// Act
useCaseManager.attach(listOf(useCase))
@@ -73,7 +88,7 @@
fun enabledUseCasesNotEmpty_whenUseCaseEnabled() {
// Arrange
val useCaseManager = createUseCaseManager()
- val useCase = Preview.Builder().build()
+ val useCase = createPreview()
useCaseManager.attach(listOf(useCase))
// Act
@@ -88,8 +103,8 @@
fun meteringRepeatingNotEnabled_whenPreviewEnabled() {
// Arrange
val useCaseManager = createUseCaseManager()
- val preview = Preview.Builder().build()
- val imageCapture = ImageCapture.Builder().build()
+ val preview = createPreview()
+ val imageCapture = createImageCapture()
useCaseManager.attach(listOf(preview, imageCapture))
// Act
@@ -105,7 +120,7 @@
fun meteringRepeatingEnabled_whenOnlyImageCaptureEnabled() {
// Arrange
val useCaseManager = createUseCaseManager()
- val imageCapture = ImageCapture.Builder().build()
+ val imageCapture = createImageCapture()
useCaseManager.attach(listOf(imageCapture))
// Act
@@ -123,12 +138,12 @@
fun meteringRepeatingDisabled_whenPreviewBecomesEnabled() {
// Arrange
val useCaseManager = createUseCaseManager()
- val imageCapture = ImageCapture.Builder().build()
+ val imageCapture = createImageCapture()
useCaseManager.attach(listOf(imageCapture))
useCaseManager.activate(imageCapture)
// Act
- val preview = Preview.Builder().build()
+ val preview = createPreview()
useCaseManager.attach(listOf(preview))
useCaseManager.activate(preview)
@@ -141,8 +156,8 @@
fun meteringRepeatingEnabled_afterAllUseCasesButImageCaptureDisabled() {
// Arrange
val useCaseManager = createUseCaseManager()
- val preview = Preview.Builder().build()
- val imageCapture = ImageCapture.Builder().build()
+ val preview = createPreview()
+ val imageCapture = createImageCapture()
useCaseManager.attach(listOf(preview, imageCapture))
useCaseManager.activate(preview)
useCaseManager.activate(imageCapture)
@@ -162,7 +177,7 @@
fun meteringRepeatingDisabled_whenAllUseCasesDisabled() {
// Arrange
val useCaseManager = createUseCaseManager()
- val imageCapture = ImageCapture.Builder().build()
+ val imageCapture = createImageCapture()
useCaseManager.attach(listOf(imageCapture))
useCaseManager.activate(imageCapture)
@@ -187,6 +202,36 @@
ComboRequestListener()
),
cameraStateAdapter = CameraStateAdapter(),
- displayInfoManager = DisplayInfoManager(ApplicationProvider.getApplicationContext()),
- )
+ displayInfoManager = DisplayInfoManager(ApplicationProvider.getApplicationContext())
+ ).also {
+ useCaseManagerList.add(it)
+ }
+
+ private fun createImageCapture(): ImageCapture =
+ ImageCapture.Builder()
+ .setCaptureOptionUnpacker { _, _ -> }
+ .setSessionOptionUnpacker() { _, _ -> }
+ .build().also {
+ it.simulateActivation()
+ useCaseList.add(it)
+ }
+
+ private fun createPreview(): Preview =
+ Preview.Builder()
+ .setCaptureOptionUnpacker { _, _ -> }
+ .setSessionOptionUnpacker() { _, _ -> }
+ .build().apply {
+ setSurfaceProvider(
+ CameraXExecutors.mainThreadExecutor(),
+ SurfaceTextureProvider.createSurfaceTextureProvider()
+ )
+ }.also {
+ it.simulateActivation()
+ useCaseList.add(it)
+ }
+
+ private fun UseCase.simulateActivation() {
+ onAttach(FakeCamera("0"), null, null)
+ updateSuggestedResolution(Size(640, 480))
+ }
}