1 /* <lambda>null2 * Copyright 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package androidx.camera.view 18 19 import android.content.Context 20 import android.graphics.Matrix 21 import android.graphics.PointF 22 import android.os.Build 23 import android.os.Looper.getMainLooper 24 import android.util.Range 25 import android.util.Rational 26 import android.util.Size 27 import android.view.Surface 28 import androidx.camera.core.AspectRatio.RATIO_16_9 29 import androidx.camera.core.AspectRatio.RATIO_4_3 30 import androidx.camera.core.CameraSelector 31 import androidx.camera.core.DynamicRange 32 import androidx.camera.core.FocusMeteringAction 33 import androidx.camera.core.FocusMeteringResult 34 import androidx.camera.core.ImageAnalysis 35 import androidx.camera.core.ImageAnalysis.COORDINATE_SYSTEM_ORIGINAL 36 import androidx.camera.core.ImageAnalysis.COORDINATE_SYSTEM_VIEW_REFERENCED 37 import androidx.camera.core.ImageCapture 38 import androidx.camera.core.ImageCapture.FLASH_MODE_ON 39 import androidx.camera.core.ImageCapture.ScreenFlash 40 import androidx.camera.core.ImageProxy 41 import androidx.camera.core.MirrorMode 42 import androidx.camera.core.Preview.SurfaceProvider 43 import androidx.camera.core.SurfaceOrientedMeteringPointFactory 44 import androidx.camera.core.TorchState 45 import androidx.camera.core.ViewPort 46 import androidx.camera.core.impl.ImageAnalysisConfig 47 import androidx.camera.core.impl.ImageCaptureConfig 48 import androidx.camera.core.impl.ImageOutputConfig 49 import androidx.camera.core.impl.utils.executor.CameraXExecutors.directExecutor 50 import androidx.camera.core.impl.utils.executor.CameraXExecutors.mainThreadExecutor 51 import androidx.camera.core.resolutionselector.AspectRatioStrategy 52 import androidx.camera.core.resolutionselector.ResolutionSelector 53 import androidx.camera.testing.fakes.FakeCamera 54 import androidx.camera.testing.fakes.FakeCameraControl 55 import androidx.camera.testing.impl.fakes.FakeLifecycleOwner 56 import androidx.camera.testing.impl.fakes.FakeSurfaceEffect 57 import androidx.camera.testing.impl.fakes.FakeSurfaceProcessor 58 import androidx.camera.video.Quality 59 import androidx.camera.video.QualitySelector 60 import androidx.camera.view.CameraController.TAP_TO_FOCUS_FOCUSED 61 import androidx.camera.view.CameraController.TAP_TO_FOCUS_NOT_FOCUSED 62 import androidx.camera.view.CameraController.TAP_TO_FOCUS_NOT_STARTED 63 import androidx.camera.view.CameraController.TAP_TO_FOCUS_STARTED 64 import androidx.camera.view.internal.ScreenFlashUiInfo 65 import androidx.concurrent.futures.CallbackToFutureAdapter 66 import androidx.test.annotation.UiThreadTest 67 import androidx.test.core.app.ApplicationProvider 68 import com.google.common.truth.Truth.assertThat 69 import com.google.common.util.concurrent.MoreExecutors 70 import java.util.concurrent.Executors 71 import java.util.concurrent.TimeUnit 72 import org.junit.Assert 73 import org.junit.Assume.assumeTrue 74 import org.junit.Before 75 import org.junit.Test 76 import org.junit.runner.RunWith 77 import org.robolectric.RobolectricTestRunner 78 import org.robolectric.Shadows.shadowOf 79 import org.robolectric.annotation.Config 80 import org.robolectric.annotation.internal.DoNotInstrument 81 import org.robolectric.shadows.ShadowSystemClock 82 83 /** Unit tests for [CameraController]. */ 84 @RunWith(RobolectricTestRunner::class) 85 @DoNotInstrument 86 @Config( 87 minSdk = Build.VERSION_CODES.LOLLIPOP, 88 instrumentedPackages = ["androidx.camera.view"], // required for shadow clock to work 89 ) 90 class CameraControllerTest { 91 companion object { 92 const val LINEAR_ZOOM = .1F 93 const val ZOOM_RATIO = .5F 94 const val TORCH_ENABLED = true 95 const val FOCUS_AUTO_CANCEL_DEFAULT_TIMEOUT_MILLIS = 96 FocusMeteringAction.DEFAULT_AUTO_CANCEL_DURATION_MILLIS 97 val TAP_POINT_1 = PointF(0F, 0F) 98 val TAP_POINT_2 = PointF(1F, 2F) 99 } 100 101 private val previewViewTransform = Matrix().also { it.postRotate(90F) } 102 private val context = ApplicationProvider.getApplicationContext<Context>() 103 private lateinit var controller: LifecycleCameraController 104 105 @Suppress("deprecation") 106 private val targetSizeWithAspectRatio = CameraController.OutputSize(RATIO_16_9) 107 private val resolutionSelector = 108 ResolutionSelector.Builder() 109 .setAspectRatioStrategy(AspectRatioStrategy.RATIO_16_9_FALLBACK_AUTO_STRATEGY) 110 .build() 111 112 @Suppress("deprecation") 113 private val targetSizeWithResolution = CameraController.OutputSize(Size(1080, 1960)) 114 private val targetVideoQuality = Quality.HIGHEST 115 private val fakeViewPort = ViewPort.Builder(Rational(1, 1), Surface.ROTATION_0).build() 116 private val fakeCameraControl = FakeCameraControl() 117 private val fakeCamera = FakeCamera(fakeCameraControl) 118 private val processCameraProviderWrapper = FakeProcessCameraProviderWrapper(fakeCamera) 119 private lateinit var lifecycleCameraProviderCompleter: 120 CallbackToFutureAdapter.Completer<ProcessCameraProviderWrapper> 121 122 private val pointFactory = SurfaceOrientedMeteringPointFactory(1f, 1f) 123 124 @Before 125 fun setUp() { 126 val lifecycleCameraProviderFuture = 127 CallbackToFutureAdapter.getFuture { completer -> 128 lifecycleCameraProviderCompleter = completer 129 "CameraControllerTest.lifecycleCameraProviderFuture" 130 } 131 controller = LifecycleCameraController(context, lifecycleCameraProviderFuture) 132 controller.bindToLifecycle(FakeLifecycleOwner()) 133 controller.attachPreviewSurface({}, fakeViewPort) 134 } 135 136 @Test 137 fun setEffects_unbindInvoked() { 138 // Arrange. 139 completeCameraInitialization() 140 assertThat(processCameraProviderWrapper.unbindInvoked()).isFalse() 141 // Act. 142 controller.setEffects( 143 setOf(FakeSurfaceEffect(directExecutor(), FakeSurfaceProcessor(directExecutor()))) 144 ) 145 // Assert. 146 assertThat(processCameraProviderWrapper.unbindInvoked()).isTrue() 147 } 148 149 @Test 150 fun clearEffects_unbindInvoked() { 151 // Arrange. 152 completeCameraInitialization() 153 assertThat(processCameraProviderWrapper.unbindInvoked()).isFalse() 154 // Act. 155 controller.clearEffects() 156 // Assert. 157 assertThat(processCameraProviderWrapper.unbindInvoked()).isTrue() 158 } 159 160 @Test 161 fun setPendingValues_valuesPropagateAfterInit() { 162 // Arrange: set pending values 163 val linearZoomFuture = controller.setLinearZoom(LINEAR_ZOOM) 164 val zoomRatioFuture = controller.setZoomRatio(ZOOM_RATIO) 165 val torchFuture = controller.enableTorch(TORCH_ENABLED) 166 assertThat(fakeCameraControl.linearZoom).isNotEqualTo(LINEAR_ZOOM) 167 assertThat(fakeCameraControl.zoomRatio).isNotEqualTo(ZOOM_RATIO) 168 assertThat(fakeCameraControl.torchEnabled).isNotEqualTo(TORCH_ENABLED) 169 assertThat(linearZoomFuture.isDone).isFalse() 170 assertThat(zoomRatioFuture.isDone).isFalse() 171 assertThat(torchFuture.isDone).isFalse() 172 173 // Act. 174 completeCameraInitialization() 175 176 // Assert: 177 assertThat(fakeCameraControl.linearZoom).isEqualTo(LINEAR_ZOOM) 178 assertThat(fakeCameraControl.zoomRatio).isEqualTo(ZOOM_RATIO) 179 assertThat(fakeCameraControl.torchEnabled).isEqualTo(TORCH_ENABLED) 180 assertThat(linearZoomFuture.isDone).isTrue() 181 assertThat(zoomRatioFuture.isDone).isTrue() 182 assertThat(torchFuture.isDone).isTrue() 183 } 184 185 @Test 186 fun unbindController_canSetPendingValueAgain() { 187 // Arrange: set pending values 188 var linearZoomFuture = controller.setLinearZoom(LINEAR_ZOOM) 189 190 // Act: complete initialization. 191 completeCameraInitialization() 192 // Assert: pending value is set. 193 assertThat(fakeCameraControl.linearZoom).isEqualTo(LINEAR_ZOOM) 194 assertThat(linearZoomFuture.isDone).isTrue() 195 196 // Act: unbind controller, set pending value again and rebind. 197 controller.unbind() 198 linearZoomFuture = controller.setLinearZoom(1F) 199 controller.bindToLifecycle(FakeLifecycleOwner()) 200 // Assert: pending value is set to new value. 201 assertThat(fakeCameraControl.linearZoom).isEqualTo(1F) 202 assertThat(linearZoomFuture.isDone).isTrue() 203 } 204 205 @Test 206 fun initCompletes_torchStatePropagated() { 207 // Arrange: get LiveData before init completes 208 val torchState = controller.torchState 209 // State is null. 210 assertThat(torchState.value).isNull() 211 // Act: complete initialization. 212 completeCameraInitialization() 213 // Assert: LiveData gets a value update. 214 assertThat(torchState.value).isEqualTo(TorchState.OFF) 215 } 216 217 @Test 218 fun initCompletes_zoomStatePropagated() { 219 // Arrange: get LiveData before init completes 220 val zoomState = controller.zoomState 221 // State is null. 222 assertThat(zoomState.value).isNull() 223 // Act: complete initialization. 224 completeCameraInitialization() 225 // Assert: LiveData gets a value update. 226 assertThat(zoomState.value).isEqualTo(fakeCamera.cameraInfo.zoomState.value) 227 } 228 229 private fun completeCameraInitialization() { 230 lifecycleCameraProviderCompleter.set(processCameraProviderWrapper) 231 shadowOf(getMainLooper()).idle() 232 } 233 234 @Test 235 fun setAnalyzerWithNewResolutionOverride_imageAnalysisIsRecreated() { 236 // Arrange: record the original ImageAnalysis 237 val originalImageAnalysis = controller.mImageAnalysis 238 // Act: set a Analyzer with overridden size. 239 controller.setImageAnalysisAnalyzer(mainThreadExecutor(), createAnalyzer(Size(1, 1))) 240 // Assert: the ImageAnalysis has be recreated. 241 assertThat(controller.mImageAnalysis).isNotEqualTo(originalImageAnalysis) 242 val newImageAnalysis = controller.mImageAnalysis 243 // Act: set a Analyzer with a different overridden size. 244 controller.setImageAnalysisAnalyzer(mainThreadExecutor(), createAnalyzer(Size(1, 2))) 245 // Assert: the ImageAnalysis has be recreated, again. 246 assertThat(controller.mImageAnalysis).isNotEqualTo(newImageAnalysis) 247 } 248 249 @Test 250 fun clearAnalyzerWithResolutionOverride_imageAnalysisIsRecreated() { 251 // Arrange: set a Analyzer with resolution and record the ImageAnalysis. 252 controller.setImageAnalysisAnalyzer(mainThreadExecutor(), createAnalyzer(Size(1, 1))) 253 val originalImageAnalysis = controller.mImageAnalysis 254 // Act: clear Analyzer 255 controller.clearImageAnalysisAnalyzer() 256 // Assert: the ImageAnalysis has been recreated. 257 assertThat(controller.mImageAnalysis).isNotEqualTo(originalImageAnalysis) 258 } 259 260 @Test 261 fun setAnalyzerWithNoOverride_imageAnalysisIsNotRecreated() { 262 // Arrange: record the original ImageAnalysis 263 val originalImageAnalysis = controller.mImageAnalysis 264 // Act: setAnalyzer with no resolution. 265 controller.setImageAnalysisAnalyzer(mainThreadExecutor(), createAnalyzer(null)) 266 // Assert: the ImageAnalysis is the same. 267 assertThat(controller.mImageAnalysis).isEqualTo(originalImageAnalysis) 268 } 269 270 @Test 271 fun setAnalysisFormat_setSuccessfully() { 272 // Act: set the format to RGBA. 273 controller.imageAnalysisOutputImageFormat = ImageAnalysis.OUTPUT_IMAGE_FORMAT_RGBA_8888 274 // Assert: returned format is RGBA. 275 assertThat(controller.imageAnalysisOutputImageFormat) 276 .isEqualTo(ImageAnalysis.OUTPUT_IMAGE_FORMAT_RGBA_8888) 277 } 278 279 /** Creates a [ImageAnalysis.Analyzer] with the given resolution override. */ 280 private fun createAnalyzer(size: Size?): ImageAnalysis.Analyzer { 281 return object : ImageAnalysis.Analyzer { 282 override fun analyze(image: ImageProxy) { 283 // no-op 284 } 285 286 override fun getDefaultTargetResolution(): Size? { 287 return size 288 } 289 } 290 } 291 292 @Test 293 fun viewTransform_valueIsPassedToAnalyzer() { 294 // Non-null value passed to analyzer. 295 assertThat( 296 getPreviewTransformPassedToAnalyzer( 297 COORDINATE_SYSTEM_VIEW_REFERENCED, 298 previewViewTransform 299 ) 300 ) 301 .isEqualTo(previewViewTransform) 302 303 // Null value passed to analyzer. 304 assertThat(getPreviewTransformPassedToAnalyzer(COORDINATE_SYSTEM_VIEW_REFERENCED, null)) 305 .isEqualTo(null) 306 } 307 308 @Test 309 fun originalTransform_valueIsNotPassedToAnalyzer() { 310 // Value not passed to analyzer. Analyzer still has it's original value which is identity 311 // matrix. 312 assertThat( 313 getPreviewTransformPassedToAnalyzer( 314 COORDINATE_SYSTEM_ORIGINAL, 315 previewViewTransform 316 )!! 317 .isIdentity 318 ) 319 .isTrue() 320 } 321 322 private fun getPreviewTransformPassedToAnalyzer( 323 coordinateSystem: Int, 324 previewTransform: Matrix? 325 ): Matrix? { 326 var matrix: Matrix? = Matrix() 327 val analyzer = 328 object : ImageAnalysis.Analyzer { 329 override fun analyze(image: ImageProxy) { 330 // no-op 331 } 332 333 override fun updateTransform(newMatrix: Matrix?) { 334 matrix = newMatrix 335 } 336 337 override fun getTargetCoordinateSystem(): Int { 338 return coordinateSystem 339 } 340 } 341 controller.setImageAnalysisAnalyzer(mainThreadExecutor(), analyzer) 342 controller.updatePreviewViewTransform(previewTransform) 343 return matrix 344 } 345 346 @UiThreadTest 347 @Test 348 fun setPreviewResolutionSelector() { 349 controller.previewResolutionSelector = resolutionSelector 350 assertThat(controller.previewResolutionSelector).isEqualTo(resolutionSelector) 351 352 val config = controller.mPreview.currentConfig as ImageOutputConfig 353 assertThat(config.resolutionSelector).isEqualTo(resolutionSelector) 354 } 355 356 @UiThreadTest 357 @Test 358 fun setAnalysisResolutionSelector() { 359 controller.imageAnalysisResolutionSelector = resolutionSelector 360 assertThat(controller.imageAnalysisResolutionSelector).isEqualTo(resolutionSelector) 361 362 val config = controller.mImageAnalysis.currentConfig as ImageOutputConfig 363 assertThat(config.resolutionSelector).isEqualTo(resolutionSelector) 364 } 365 366 @UiThreadTest 367 @Test 368 fun setImageCaptureResolutionSelector() { 369 controller.imageCaptureResolutionSelector = resolutionSelector 370 assertThat(controller.imageCaptureResolutionSelector).isEqualTo(resolutionSelector) 371 372 val config = controller.mImageCapture.currentConfig as ImageOutputConfig 373 assertThat(config.resolutionSelector).isEqualTo(resolutionSelector) 374 } 375 376 @UiThreadTest 377 @Test 378 @Suppress("deprecation") 379 fun setPreviewAspectRatio() { 380 controller.previewTargetSize = targetSizeWithAspectRatio 381 assertThat(controller.previewTargetSize).isEqualTo(targetSizeWithAspectRatio) 382 383 val config = controller.mPreview.currentConfig as ImageOutputConfig 384 assertThat(config.targetAspectRatio).isEqualTo(targetSizeWithAspectRatio.aspectRatio) 385 } 386 387 @UiThreadTest 388 @Test 389 @Suppress("deprecation") 390 fun setPreviewResolution() { 391 controller.previewTargetSize = targetSizeWithResolution 392 assertThat(controller.previewTargetSize).isEqualTo(targetSizeWithResolution) 393 394 val config = controller.mPreview.currentConfig as ImageOutputConfig 395 assertThat(config.targetResolution).isEqualTo(targetSizeWithResolution.resolution) 396 } 397 398 @UiThreadTest 399 @Test 400 @Suppress("deprecation") 401 fun setAnalysisAspectRatio() { 402 controller.imageAnalysisTargetSize = targetSizeWithAspectRatio 403 assertThat(controller.imageAnalysisTargetSize).isEqualTo(targetSizeWithAspectRatio) 404 405 val config = controller.mImageAnalysis.currentConfig as ImageOutputConfig 406 assertThat(config.targetAspectRatio).isEqualTo(targetSizeWithAspectRatio.aspectRatio) 407 } 408 409 @UiThreadTest 410 @Test 411 fun setAnalysisBackgroundExecutor() { 412 val executor = Executors.newSingleThreadExecutor() 413 controller.imageAnalysisBackgroundExecutor = executor 414 assertThat(controller.imageAnalysisBackgroundExecutor).isEqualTo(executor) 415 val config = controller.mImageAnalysis.currentConfig as ImageAnalysisConfig 416 assertThat(config.backgroundExecutor).isEqualTo(executor) 417 } 418 419 @UiThreadTest 420 @Test 421 fun setAnalysisQueueDepth() { 422 controller.imageAnalysisImageQueueDepth = 100 423 assertThat(controller.imageAnalysisImageQueueDepth).isEqualTo(100) 424 assertThat(controller.mImageAnalysis.imageQueueDepth).isEqualTo(100) 425 } 426 427 @UiThreadTest 428 @Test 429 fun setAnalysisBackpressureStrategy() { 430 controller.imageAnalysisBackpressureStrategy = ImageAnalysis.STRATEGY_BLOCK_PRODUCER 431 assertThat(controller.imageAnalysisBackpressureStrategy) 432 .isEqualTo(ImageAnalysis.STRATEGY_BLOCK_PRODUCER) 433 assertThat(controller.mImageAnalysis.backpressureStrategy) 434 .isEqualTo(ImageAnalysis.STRATEGY_BLOCK_PRODUCER) 435 } 436 437 @UiThreadTest 438 @Test 439 @Suppress("deprecation") 440 fun setImageCaptureResolution() { 441 controller.imageCaptureTargetSize = targetSizeWithResolution 442 assertThat(controller.imageCaptureTargetSize).isEqualTo(targetSizeWithResolution) 443 444 val config = controller.mImageCapture.currentConfig as ImageOutputConfig 445 assertThat(config.targetResolution).isEqualTo(targetSizeWithResolution.resolution) 446 } 447 448 @UiThreadTest 449 @Test 450 @Suppress("deprecation") 451 fun setImageCaptureAspectRatio() { 452 controller.imageCaptureTargetSize = targetSizeWithAspectRatio 453 assertThat(controller.imageCaptureTargetSize).isEqualTo(targetSizeWithAspectRatio) 454 455 val config = controller.mImageCapture.currentConfig as ImageOutputConfig 456 assertThat(config.targetAspectRatio).isEqualTo(targetSizeWithAspectRatio.aspectRatio) 457 } 458 459 @UiThreadTest 460 @Test 461 fun setImageCaptureMode() { 462 controller.imageCaptureMode = ImageCapture.CAPTURE_MODE_MAXIMIZE_QUALITY 463 assertThat(controller.imageCaptureMode) 464 .isEqualTo(ImageCapture.CAPTURE_MODE_MAXIMIZE_QUALITY) 465 assertThat(controller.mImageCapture.captureMode) 466 .isEqualTo(ImageCapture.CAPTURE_MODE_MAXIMIZE_QUALITY) 467 } 468 469 @UiThreadTest 470 @Test 471 fun setImageCaptureIoExecutor() { 472 val ioExecutor = Executors.newSingleThreadExecutor() 473 controller.imageCaptureIoExecutor = ioExecutor 474 assertThat(controller.imageCaptureIoExecutor).isEqualTo(ioExecutor) 475 val config = controller.mImageCapture.currentConfig as ImageCaptureConfig 476 assertThat(config.ioExecutor).isEqualTo(ioExecutor) 477 } 478 479 @UiThreadTest 480 @Test 481 fun setVideoCaptureQuality() { 482 val qualitySelector = QualitySelector.from(targetVideoQuality) 483 controller.videoCaptureQualitySelector = qualitySelector 484 assertThat(controller.videoCaptureQualitySelector).isEqualTo(qualitySelector) 485 } 486 487 @UiThreadTest 488 @Test 489 fun setVideoCaptureMirrorMode() { 490 controller.videoCaptureMirrorMode = MirrorMode.MIRROR_MODE_ON_FRONT_ONLY 491 assertThat(controller.videoCaptureMirrorMode) 492 .isEqualTo(MirrorMode.MIRROR_MODE_ON_FRONT_ONLY) 493 assertThat(controller.mVideoCapture.mirrorMode) 494 .isEqualTo(MirrorMode.MIRROR_MODE_ON_FRONT_ONLY) 495 } 496 497 @UiThreadTest 498 @Test 499 fun setVideoCaptureDynamicRange() { 500 controller.videoCaptureDynamicRange = DynamicRange.HDR10_10_BIT 501 assertThat(controller.videoCaptureDynamicRange).isEqualTo(DynamicRange.HDR10_10_BIT) 502 assertThat(controller.mVideoCapture.dynamicRange).isEqualTo(DynamicRange.HDR10_10_BIT) 503 } 504 505 @UiThreadTest 506 @Test 507 fun setPreviewDynamicRange() { 508 controller.previewDynamicRange = DynamicRange.HDR10_10_BIT 509 assertThat(controller.previewDynamicRange).isEqualTo(DynamicRange.HDR10_10_BIT) 510 assertThat(controller.mPreview.dynamicRange).isEqualTo(DynamicRange.HDR10_10_BIT) 511 } 512 513 @UiThreadTest 514 @Test 515 fun setVideoCaptureFrameRate() { 516 controller.videoCaptureTargetFrameRate = Range.create(60, 120) 517 assertThat(controller.videoCaptureTargetFrameRate).isEqualTo(Range.create(60, 120)) 518 assertThat(controller.mVideoCapture.targetFrameRate).isEqualTo(Range.create(60, 120)) 519 } 520 521 @UiThreadTest 522 @Test 523 fun sensorRotationChanges_useCaseTargetRotationUpdated() { 524 // Act. 525 controller.mDeviceRotationListener.onRotationChanged(Surface.ROTATION_180) 526 527 // Assert. 528 assertThat(controller.mImageAnalysis.targetRotation).isEqualTo(Surface.ROTATION_180) 529 assertThat(controller.mImageCapture.targetRotation).isEqualTo(Surface.ROTATION_180) 530 val videoConfig = controller.mVideoCapture.currentConfig as ImageOutputConfig 531 assertThat(videoConfig.targetRotation).isEqualTo(Surface.ROTATION_180) 532 } 533 534 @UiThreadTest 535 @Test 536 fun setSelectorBeforeBound_selectorSet() { 537 // Arrange. 538 assertThat(controller.cameraSelector.lensFacing).isEqualTo(CameraSelector.LENS_FACING_BACK) 539 540 // Act. 541 controller.cameraSelector = CameraSelector.DEFAULT_FRONT_CAMERA 542 543 // Assert. 544 assertThat(controller.cameraSelector.lensFacing).isEqualTo(CameraSelector.LENS_FACING_FRONT) 545 } 546 547 @Test 548 fun throwsException_whenScreenFlashModeSetWithBackCamera() { 549 controller.cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA 550 551 Assert.assertThrows(IllegalArgumentException::class.java) { 552 controller.imageCaptureFlashMode = ImageCapture.FLASH_MODE_SCREEN 553 } 554 } 555 556 @Test 557 fun canSetScreenFlashMode_whenScreenFlashUiInfoNotSetYet() { 558 controller.cameraSelector = CameraSelector.DEFAULT_FRONT_CAMERA 559 controller.imageCaptureFlashMode = ImageCapture.FLASH_MODE_SCREEN 560 561 assertThat(controller.imageCaptureFlashMode).isEqualTo(ImageCapture.FLASH_MODE_SCREEN) 562 } 563 564 @Test 565 fun canTakePictureWithScreenFlashMode_whenFrontCameraAndScreenFlashUiInfoSet() { 566 controller.cameraSelector = CameraSelector.DEFAULT_FRONT_CAMERA 567 controller.setScreenFlashUiInfo( 568 ScreenFlashUiInfo( 569 ScreenFlashUiInfo.ProviderType.SCREEN_FLASH_VIEW, 570 object : ScreenFlash { 571 override fun apply( 572 expirationTimeMillis: Long, 573 screenFlashListener: ImageCapture.ScreenFlashListener, 574 ) { 575 screenFlashListener.onCompleted() 576 } 577 578 override fun clear() {} 579 } 580 ) 581 ) 582 583 controller.imageCaptureFlashMode = ImageCapture.FLASH_MODE_SCREEN 584 completeCameraInitialization() 585 586 controller.takePicture( 587 MoreExecutors.directExecutor(), 588 object : ImageCapture.OnImageCapturedCallback() {} 589 ) 590 591 // ensure FLASH_MODE_SCREEN was retained 592 assertThat(controller.imageCaptureFlashMode).isEqualTo(ImageCapture.FLASH_MODE_SCREEN) 593 } 594 595 @Test 596 fun throwException_whenTakePictureWithScreenFlashModeButWithoutScreenFlashUiInfo() { 597 controller.cameraSelector = CameraSelector.DEFAULT_FRONT_CAMERA 598 controller.imageCaptureFlashMode = ImageCapture.FLASH_MODE_SCREEN 599 completeCameraInitialization() 600 601 Assert.assertThrows(IllegalStateException::class.java) { 602 controller.takePicture( 603 MoreExecutors.directExecutor(), 604 object : ImageCapture.OnImageCapturedCallback() {} 605 ) 606 } 607 } 608 609 @Test 610 fun throwsException_whenSwitchToBackCameraAfterScreenFlashSetToFrontCamera() { 611 controller.cameraSelector = CameraSelector.DEFAULT_FRONT_CAMERA 612 controller.imageCaptureFlashMode = ImageCapture.FLASH_MODE_SCREEN 613 614 Assert.assertThrows(IllegalStateException::class.java) { 615 controller.cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA 616 } 617 } 618 619 @UiThreadTest 620 @Test 621 fun preview_surfaceProviderIsPreserved_afterRebind() { 622 // Arrange. 623 val surfaceProvider = SurfaceProvider {} 624 controller.attachPreviewSurface(surfaceProvider, fakeViewPort) 625 626 // Act: Setting a different resolution selector triggers a rebinding. 627 controller.previewResolutionSelector = resolutionSelector 628 629 // Assert. 630 assertThat(controller.mPreview.surfaceProvider).isSameInstanceAs(surfaceProvider) 631 } 632 633 @UiThreadTest 634 @Test 635 fun imageCapture_flashModeIsPreserved_afterRebind() { 636 // Arrange. 637 controller.imageCaptureFlashMode = FLASH_MODE_ON 638 639 // Act: Setting a different resolution selector triggers a rebinding. 640 controller.imageCaptureResolutionSelector = resolutionSelector 641 642 // Assert. 643 assertThat(controller.imageCaptureFlashMode).isEqualTo(FLASH_MODE_ON) 644 } 645 646 @Suppress("deprecation") 647 @Test 648 fun setResolutionSelectorAndOutputSizeAtTheSameTime() { 649 // Arrange & Act: Set resolution selector and target size together. 650 controller.previewResolutionSelector = resolutionSelector 651 controller.imageCaptureResolutionSelector = resolutionSelector 652 controller.imageAnalysisResolutionSelector = resolutionSelector 653 controller.previewTargetSize = targetSizeWithResolution 654 controller.imageCaptureTargetSize = targetSizeWithResolution 655 controller.imageAnalysisTargetSize = targetSizeWithResolution 656 657 // Assert: The resolution selector should be set, while the target resolution should not. 658 val previewConfig = controller.mPreview.currentConfig as ImageOutputConfig 659 assertThat(previewConfig.resolutionSelector).isEqualTo(resolutionSelector) 660 assertThat(previewConfig.getTargetResolution(null)).isNull() 661 val imageCaptureConfig = controller.mImageCapture.currentConfig as ImageOutputConfig 662 assertThat(imageCaptureConfig.resolutionSelector).isEqualTo(resolutionSelector) 663 assertThat(imageCaptureConfig.getTargetResolution(null)).isNull() 664 val imageAnalysisConfig = controller.mImageAnalysis.currentConfig as ImageOutputConfig 665 assertThat(imageAnalysisConfig.resolutionSelector).isEqualTo(resolutionSelector) 666 assertThat(imageAnalysisConfig.getTargetResolution(null)).isNull() 667 } 668 669 @Test 670 fun setViewport_overrideUseCasesAspectRatio() { 671 // Arrange & Act: Set a 16:9 viewport. 672 controller.attachPreviewSurface( 673 {}, 674 ViewPort.Builder(Rational(9, 16), Surface.ROTATION_90).build() 675 ) 676 677 // Assert: The aspect ratio of the use case configs should be override by viewport, 678 // which should be 16:9. 679 val previewConfig = controller.mPreview.currentConfig as ImageOutputConfig 680 assertThat(previewConfig.resolutionSelector.aspectRatioStrategy.preferredAspectRatio) 681 .isEqualTo(RATIO_16_9) 682 val imageCaptureConfig = controller.mImageCapture.currentConfig as ImageOutputConfig 683 assertThat(imageCaptureConfig.resolutionSelector.aspectRatioStrategy.preferredAspectRatio) 684 .isEqualTo(RATIO_16_9) 685 val imageAnalysisConfig = controller.mImageAnalysis.currentConfig as ImageOutputConfig 686 assertThat(imageAnalysisConfig.resolutionSelector.aspectRatioStrategy.preferredAspectRatio) 687 .isEqualTo(RATIO_16_9) 688 assertThat(controller.mVideoCapture.output.aspectRatio).isEqualTo(RATIO_16_9) 689 } 690 691 @Test 692 fun setViewport_notOverrideUseCasesAspectRatioIfResolutionSelectorAlreadySet() { 693 // Arrange: Set a 4:3 viewport. 694 controller.attachPreviewSurface( 695 {}, 696 ViewPort.Builder(Rational(4, 3), Surface.ROTATION_0).build() 697 ) 698 699 // Act: Explicitly set a 16:9 resolution selector. 700 controller.previewResolutionSelector = resolutionSelector 701 controller.imageCaptureResolutionSelector = resolutionSelector 702 controller.imageAnalysisResolutionSelector = resolutionSelector 703 controller.videoCaptureQualitySelector = QualitySelector.from(targetVideoQuality) 704 705 // Assert: The aspect ratio of the use case configs should not be override. 706 val previewConfig = controller.mPreview.currentConfig as ImageOutputConfig 707 assertThat(previewConfig.resolutionSelector.aspectRatioStrategy.preferredAspectRatio) 708 .isNotEqualTo(RATIO_4_3) 709 val imageCaptureConfig = controller.mImageCapture.currentConfig as ImageOutputConfig 710 assertThat(imageCaptureConfig.resolutionSelector.aspectRatioStrategy.preferredAspectRatio) 711 .isNotEqualTo(RATIO_4_3) 712 val imageAnalysisConfig = controller.mImageAnalysis.currentConfig as ImageOutputConfig 713 assertThat(imageAnalysisConfig.resolutionSelector.aspectRatioStrategy.preferredAspectRatio) 714 .isNotEqualTo(RATIO_4_3) 715 assertThat(controller.mVideoCapture.output.aspectRatio).isNotEqualTo(RATIO_4_3) 716 } 717 718 @Suppress("deprecation") 719 @Test 720 fun setViewport_notOverrideUseCasesAspectRatioIfOutputSizeAlreadySet() { 721 // Arrange: Set a 4:3 viewport. 722 controller.attachPreviewSurface( 723 {}, 724 ViewPort.Builder(Rational(4, 3), Surface.ROTATION_0).build() 725 ) 726 727 // Act: Explicitly set a 16:9 target size. 728 controller.previewTargetSize = targetSizeWithAspectRatio 729 controller.imageCaptureTargetSize = targetSizeWithAspectRatio 730 controller.imageAnalysisTargetSize = targetSizeWithAspectRatio 731 732 // Assert: The resolution selector should not exist in the config. 733 val previewConfig = controller.mPreview.currentConfig as ImageOutputConfig 734 assertThat(previewConfig.getResolutionSelector(null)).isNull() 735 val imageCaptureConfig = controller.mImageCapture.currentConfig as ImageOutputConfig 736 assertThat(imageCaptureConfig.getResolutionSelector(null)).isNull() 737 val imageAnalysisConfig = controller.mImageAnalysis.currentConfig as ImageOutputConfig 738 assertThat(imageAnalysisConfig.getResolutionSelector(null)).isNull() 739 } 740 741 @Test 742 fun onTapToFocus_focusMeteringActionSubmittedToCamera() { 743 completeCameraInitialization() 744 745 controller.onTapToFocus(pointFactory, 0f, 0f) 746 assertThat(fakeCameraControl.lastSubmittedFocusMeteringAction).isNotNull() 747 } 748 749 @Test 750 fun getTapToFocusInfoState_defaultStateIsTapToFocusNotStarted() { 751 assertThat(controller.tapToFocusInfoState.value?.focusState) 752 .isEqualTo(TAP_TO_FOCUS_NOT_STARTED) 753 } 754 755 @Test 756 fun getTapToFocusInfoState_tapPointIsSameAsOriginal_whenOnTapToFocusIsCalled() { 757 val tapPoint = PointF(1F, 2F) 758 completeCameraInitialization() 759 760 controller.onTapToFocus(pointFactory, tapPoint.x, tapPoint.y) 761 762 shadowOf(getMainLooper()).idle() 763 assertThat(controller.tapToFocusInfoState.value?.tapPoint).isEqualTo(tapPoint) 764 } 765 766 @Test 767 fun getTapToFocusInfoState_tapPointIsSameAsLastOne_whenOnTapToFocusIsCalledTwice() { 768 completeCameraInitialization() 769 770 controller.onTapToFocus(pointFactory, TAP_POINT_1.x, TAP_POINT_1.y) 771 controller.onTapToFocus(pointFactory, TAP_POINT_2.x, TAP_POINT_2.y) 772 773 shadowOf(getMainLooper()).idle() 774 assertThat(controller.tapToFocusInfoState.value?.tapPoint).isEqualTo(TAP_POINT_2) 775 } 776 777 @Test 778 fun getTapToFocusInfoState_stateIsTapToFocusStarted_whenOnTapToFocusIsCalled() { 779 completeCameraInitialization() 780 fakeCameraControl.disableFocusMeteringAutoComplete(true) 781 782 controller.onTapToFocus(pointFactory, 0f, 0f) 783 784 shadowOf(getMainLooper()).idle() 785 assertThat(controller.tapToFocusInfoState.value?.focusState).isEqualTo(TAP_TO_FOCUS_STARTED) 786 } 787 788 @Suppress("DEPRECATION") 789 @Test 790 fun getTapToFocusState_stateIsTapToFocusStarted_whenOnTapToFocusIsCalled() { 791 completeCameraInitialization() 792 fakeCameraControl.disableFocusMeteringAutoComplete(true) 793 794 controller.onTapToFocus(pointFactory, 0f, 0f) 795 796 shadowOf(getMainLooper()).idle() 797 assertThat(controller.tapToFocusState.value).isEqualTo(TAP_TO_FOCUS_STARTED) 798 } 799 800 @Test 801 fun getTapToFocusInfoState_stateIsTapToFocusFocused_whenSuccessfulResultIsSubmitted() { 802 completeCameraInitialization() 803 fakeCameraControl.disableFocusMeteringAutoComplete(true) 804 805 controller.onTapToFocus(pointFactory, 0f, 0f) 806 fakeCameraControl.submitFocusMeteringResult(FocusMeteringResult.create(true)) 807 808 shadowOf(getMainLooper()).idle() 809 assertThat(controller.tapToFocusInfoState.value?.focusState).isEqualTo(TAP_TO_FOCUS_FOCUSED) 810 } 811 812 @Suppress("DEPRECATION") 813 @Test 814 fun getTapToFocusState_stateIsTapToFocusFocused_whenSuccessfulResultIsSubmitted() { 815 completeCameraInitialization() 816 fakeCameraControl.disableFocusMeteringAutoComplete(true) 817 818 controller.onTapToFocus(pointFactory, 0f, 0f) 819 fakeCameraControl.submitFocusMeteringResult(FocusMeteringResult.create(true)) 820 821 shadowOf(getMainLooper()).idle() 822 assertThat(controller.tapToFocusState.value).isEqualTo(TAP_TO_FOCUS_FOCUSED) 823 } 824 825 @Test 826 fun getTapToFocusInfoState_stateIsTapToFocusNotFocused_whenUnsuccessfulResultIsSubmitted() { 827 completeCameraInitialization() 828 fakeCameraControl.disableFocusMeteringAutoComplete(true) 829 830 controller.onTapToFocus(pointFactory, 0f, 0f) 831 fakeCameraControl.submitFocusMeteringResult(FocusMeteringResult.create(false)) 832 833 shadowOf(getMainLooper()).idle() 834 assertThat(controller.tapToFocusInfoState.value?.focusState) 835 .isEqualTo(TAP_TO_FOCUS_NOT_FOCUSED) 836 } 837 838 @Suppress("DEPRECATION") 839 @Test 840 fun getTapToFocusState_stateIsTapToFocusNotFocused_whenUnsuccessfulResultIsSubmitted() { 841 completeCameraInitialization() 842 fakeCameraControl.disableFocusMeteringAutoComplete(true) 843 844 controller.onTapToFocus(pointFactory, 0f, 0f) 845 fakeCameraControl.submitFocusMeteringResult(FocusMeteringResult.create(false)) 846 847 shadowOf(getMainLooper()).idle() 848 assertThat(controller.tapToFocusState.value).isEqualTo(TAP_TO_FOCUS_NOT_FOCUSED) 849 } 850 851 @Test 852 fun getTapToFocusInfoState_stateIsTapToFocusStarted_beforeAutoCancelDurationIsElapsed() { 853 completeCameraInitialization() 854 fakeCameraControl.disableFocusMeteringAutoComplete(true) 855 856 controller.onTapToFocus(pointFactory, 0f, 0f) 857 ShadowSystemClock.advanceBy( 858 FOCUS_AUTO_CANCEL_DEFAULT_TIMEOUT_MILLIS - 1, 859 TimeUnit.MILLISECONDS 860 ) 861 862 shadowOf(getMainLooper()).idle() 863 assertThat(controller.tapToFocusInfoState.value?.focusState).isEqualTo(TAP_TO_FOCUS_STARTED) 864 } 865 866 @Suppress("DEPRECATION") 867 @Test 868 fun getTapToFocusState_stateIsTapToFocusStarted_beforeAutoCancelDurationIsElapsed() { 869 completeCameraInitialization() 870 fakeCameraControl.disableFocusMeteringAutoComplete(true) 871 872 controller.onTapToFocus(pointFactory, 0f, 0f) 873 ShadowSystemClock.advanceBy( 874 FOCUS_AUTO_CANCEL_DEFAULT_TIMEOUT_MILLIS - 1, 875 TimeUnit.MILLISECONDS 876 ) 877 878 shadowOf(getMainLooper()).idle() 879 assertThat(controller.tapToFocusState.value).isEqualTo(TAP_TO_FOCUS_STARTED) 880 } 881 882 @Test 883 fun getTapToFocusInfoState_stateIsNotStarted_whenAutoCancelDurationIsElapsedAfterStarted() { 884 completeCameraInitialization() 885 fakeCameraControl.disableFocusMeteringAutoComplete(true) 886 887 controller.onTapToFocus(pointFactory, 0f, 0f) 888 889 shadowOf(getMainLooper()).idle() 890 assumeTrue(controller.tapToFocusInfoState.value?.focusState == TAP_TO_FOCUS_STARTED) 891 892 ShadowSystemClock.advanceBy(FOCUS_AUTO_CANCEL_DEFAULT_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS) 893 894 shadowOf(getMainLooper()).idle() 895 assertThat(controller.tapToFocusInfoState.value?.focusState) 896 .isEqualTo(TAP_TO_FOCUS_NOT_STARTED) 897 } 898 899 @Suppress("DEPRECATION") 900 @Test 901 fun getTapToFocusState_stateIsNotStarted_whenAutoCancelDurationIsElapsedAfterStarted() { 902 completeCameraInitialization() 903 fakeCameraControl.disableFocusMeteringAutoComplete(true) 904 905 controller.onTapToFocus(pointFactory, 0f, 0f) 906 907 shadowOf(getMainLooper()).idle() 908 assumeTrue(controller.tapToFocusState.value == TAP_TO_FOCUS_STARTED) 909 910 ShadowSystemClock.advanceBy(FOCUS_AUTO_CANCEL_DEFAULT_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS) 911 912 shadowOf(getMainLooper()).idle() 913 assertThat(controller.tapToFocusState.value).isEqualTo(TAP_TO_FOCUS_NOT_STARTED) 914 } 915 916 @Test 917 fun getTapToFocusInfoState_stateIsNotStarted_whenAutoCancelDurationIsElapsedAfterFocused() { 918 completeCameraInitialization() 919 fakeCameraControl.disableFocusMeteringAutoComplete(true) 920 921 controller.onTapToFocus(pointFactory, 0f, 0f) 922 fakeCameraControl.submitFocusMeteringResult(FocusMeteringResult.create(true)) 923 shadowOf(getMainLooper()).idle() 924 assumeTrue(controller.tapToFocusInfoState.value?.focusState == TAP_TO_FOCUS_FOCUSED) 925 926 ShadowSystemClock.advanceBy(FOCUS_AUTO_CANCEL_DEFAULT_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS) 927 928 shadowOf(getMainLooper()).idle() 929 assertThat(controller.tapToFocusInfoState.value?.focusState) 930 .isEqualTo(TAP_TO_FOCUS_NOT_STARTED) 931 } 932 933 @Suppress("DEPRECATION") 934 @Test 935 fun getTapToFocusState_stateIsNotStarted_whenAutoCancelDurationIsElapsedAfterFocused() { 936 completeCameraInitialization() 937 fakeCameraControl.disableFocusMeteringAutoComplete(true) 938 939 controller.onTapToFocus(pointFactory, 0f, 0f) 940 fakeCameraControl.submitFocusMeteringResult(FocusMeteringResult.create(true)) 941 shadowOf(getMainLooper()).idle() 942 assumeTrue(controller.tapToFocusState.value == TAP_TO_FOCUS_FOCUSED) 943 944 ShadowSystemClock.advanceBy(FOCUS_AUTO_CANCEL_DEFAULT_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS) 945 946 shadowOf(getMainLooper()).idle() 947 assertThat(controller.tapToFocusState.value).isEqualTo(TAP_TO_FOCUS_NOT_STARTED) 948 } 949 950 @Test 951 fun getTapToFocusInfoState_stateIsNotStarted_whenAutoCancelDurationIsElapsedAfterNotFocused() { 952 completeCameraInitialization() 953 fakeCameraControl.disableFocusMeteringAutoComplete(true) 954 955 controller.onTapToFocus(pointFactory, 0f, 0f) 956 fakeCameraControl.submitFocusMeteringResult(FocusMeteringResult.create(false)) 957 shadowOf(getMainLooper()).idle() 958 assumeTrue(controller.tapToFocusInfoState.value?.focusState == TAP_TO_FOCUS_NOT_FOCUSED) 959 960 ShadowSystemClock.advanceBy(FOCUS_AUTO_CANCEL_DEFAULT_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS) 961 962 shadowOf(getMainLooper()).idle() 963 assertThat(controller.tapToFocusInfoState.value?.focusState) 964 .isEqualTo(TAP_TO_FOCUS_NOT_STARTED) 965 } 966 967 @Suppress("DEPRECATION") 968 @Test 969 fun getTapToFocusState_stateIsNotStarted_whenAutoCancelDurationIsElapsedAfterNotFocused() { 970 completeCameraInitialization() 971 fakeCameraControl.disableFocusMeteringAutoComplete(true) 972 973 controller.onTapToFocus(pointFactory, 0f, 0f) 974 fakeCameraControl.submitFocusMeteringResult(FocusMeteringResult.create(false)) 975 shadowOf(getMainLooper()).idle() 976 assumeTrue(controller.tapToFocusState.value == TAP_TO_FOCUS_NOT_FOCUSED) 977 978 ShadowSystemClock.advanceBy(FOCUS_AUTO_CANCEL_DEFAULT_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS) 979 980 shadowOf(getMainLooper()).idle() 981 assertThat(controller.tapToFocusState.value).isEqualTo(TAP_TO_FOCUS_NOT_STARTED) 982 } 983 984 @Test 985 fun setTapToFocusAutoCancelDuration_stateIsNotStarted_whenSetDurationIsElapsedAfterStarted() { 986 val autoCancelDurationSeconds = 2L 987 988 completeCameraInitialization() 989 fakeCameraControl.disableFocusMeteringAutoComplete(true) 990 991 controller.setTapToFocusAutoCancelDuration(autoCancelDurationSeconds, TimeUnit.SECONDS) 992 993 controller.onTapToFocus(pointFactory, 0f, 0f) 994 995 // Ensure focus state has is not the initial NOT_STARTED state. 996 shadowOf(getMainLooper()).idle() 997 assumeTrue(controller.tapToFocusInfoState.value?.focusState == TAP_TO_FOCUS_STARTED) 998 999 ShadowSystemClock.advanceBy(autoCancelDurationSeconds, TimeUnit.SECONDS) 1000 1001 shadowOf(getMainLooper()).idle() 1002 assertThat(controller.tapToFocusInfoState.value?.focusState) 1003 .isEqualTo(TAP_TO_FOCUS_NOT_STARTED) 1004 } 1005 1006 @Test 1007 fun setTapToFocusAutoCancelDuration_stateNeverResetsToNotStarted_whenDurationIsZero() { 1008 completeCameraInitialization() 1009 fakeCameraControl.disableFocusMeteringAutoComplete(true) 1010 1011 controller.setTapToFocusAutoCancelDuration(0, TimeUnit.SECONDS) 1012 1013 controller.onTapToFocus(pointFactory, 0f, 0f) 1014 1015 // Ensure focus state has is not the initial NOT_STARTED state. 1016 shadowOf(getMainLooper()).idle() 1017 assumeTrue(controller.tapToFocusInfoState.value?.focusState == TAP_TO_FOCUS_STARTED) 1018 1019 // Advance to end of time 1020 ShadowSystemClock.advanceBy(Long.MAX_VALUE, TimeUnit.SECONDS) 1021 1022 // State is still the previous STARTED state 1023 shadowOf(getMainLooper()).idle() 1024 assertThat(controller.tapToFocusInfoState.value?.focusState).isEqualTo(TAP_TO_FOCUS_STARTED) 1025 } 1026 1027 @Test 1028 fun getTapToFocusInfoState_stateIsStartedAfterAutoCancelTimeOfFirstTap_whenTappedTwice() { 1029 completeCameraInitialization() 1030 fakeCameraControl.disableFocusMeteringAutoComplete(true) 1031 val tapInterval = FOCUS_AUTO_CANCEL_DEFAULT_TIMEOUT_MILLIS / 2 1032 1033 controller.onTapToFocus(pointFactory, TAP_POINT_1.x, TAP_POINT_1.y) 1034 1035 // Advance the clock by `tapInterval` to that first tap is supposed to be auto-canceled at 1036 // FOCUS_AUTO_CANCEL_DEFAULT_TIMEOUT_MILLIS while 2nd tap is supposed to be canceled at 1037 // FOCUS_AUTO_CANCEL_DEFAULT_TIMEOUT_MILLIS + tapInterval 1038 ShadowSystemClock.advanceBy(tapInterval, TimeUnit.MILLISECONDS) 1039 controller.onTapToFocus(pointFactory, TAP_POINT_2.x, TAP_POINT_2.y) 1040 1041 // Advance the clock to the 1st tap cancellation time by advancing by the remaining time. 1042 ShadowSystemClock.advanceBy( 1043 FOCUS_AUTO_CANCEL_DEFAULT_TIMEOUT_MILLIS - tapInterval, 1044 TimeUnit.MILLISECONDS 1045 ) 1046 1047 shadowOf(getMainLooper()).idle() 1048 assertThat(controller.tapToFocusInfoState.value?.focusState).isEqualTo(TAP_TO_FOCUS_STARTED) 1049 } 1050 1051 @Test 1052 fun getTapToFocusInfoState_stateIsNotStartedAfterAutoCancelTimeOfSecondTap_whenTappedTwice() { 1053 completeCameraInitialization() 1054 fakeCameraControl.disableFocusMeteringAutoComplete(true) 1055 val tapInterval = FOCUS_AUTO_CANCEL_DEFAULT_TIMEOUT_MILLIS / 2 1056 1057 controller.onTapToFocus(pointFactory, TAP_POINT_1.x, TAP_POINT_1.y) 1058 1059 // Ensure focus state has is not the initial NOT_STARTED state. 1060 shadowOf(getMainLooper()).idle() 1061 assumeTrue(controller.tapToFocusInfoState.value?.focusState == TAP_TO_FOCUS_STARTED) 1062 1063 // Advance the clock by `tapInterval` to that first tap is supposed to be auto-canceled at 1064 // FOCUS_AUTO_CANCEL_DEFAULT_TIMEOUT_MILLIS while 2nd tap is supposed to be canceled at 1065 // FOCUS_AUTO_CANCEL_DEFAULT_TIMEOUT_MILLIS + tapInterval 1066 ShadowSystemClock.advanceBy(tapInterval, TimeUnit.MILLISECONDS) 1067 controller.onTapToFocus(pointFactory, TAP_POINT_2.x, TAP_POINT_2.y) 1068 1069 // Advance the clock to the 2nd tap cancellation time. 1070 ShadowSystemClock.advanceBy(FOCUS_AUTO_CANCEL_DEFAULT_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS) 1071 1072 shadowOf(getMainLooper()).idle() 1073 assertThat(controller.tapToFocusInfoState.value?.focusState) 1074 .isEqualTo(TAP_TO_FOCUS_NOT_STARTED) 1075 } 1076 } 1077