1 /* 2 * 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.extensions.internal 18 19 import android.content.Context 20 import android.graphics.ImageFormat 21 import android.hardware.camera2.CameraCharacteristics 22 import android.util.Pair 23 import android.util.Size 24 import androidx.camera.camera2.Camera2Config 25 import androidx.camera.core.CameraSelector 26 import androidx.camera.core.ImageCapture 27 import androidx.camera.core.impl.CameraInfoInternal 28 import androidx.camera.core.impl.ImageOutputConfig 29 import androidx.camera.extensions.ExtensionMode 30 import androidx.camera.extensions.impl.ImageCaptureExtenderImpl 31 import androidx.camera.lifecycle.ProcessCameraProvider 32 import androidx.camera.testing.impl.CameraUtil 33 import androidx.camera.testing.impl.CameraUtil.PreTestCameraIdList 34 import androidx.camera.testing.impl.fakes.FakeLifecycleOwner 35 import androidx.test.core.app.ApplicationProvider 36 import androidx.test.ext.junit.runners.AndroidJUnit4 37 import androidx.test.filters.MediumTest 38 import androidx.test.filters.SdkSuppress 39 import com.google.common.truth.Truth 40 import java.util.concurrent.TimeUnit 41 import kotlinx.coroutines.Dispatchers 42 import kotlinx.coroutines.runBlocking 43 import kotlinx.coroutines.withContext 44 import org.junit.After 45 import org.junit.Assume.assumeTrue 46 import org.junit.Before 47 import org.junit.Ignore 48 import org.junit.Rule 49 import org.junit.Test 50 import org.junit.runner.RunWith 51 import org.mockito.ArgumentMatchers.any 52 import org.mockito.Mockito 53 import org.mockito.Mockito.mock 54 55 @MediumTest 56 @RunWith(AndroidJUnit4::class) 57 @SdkSuppress(minSdkVersion = 23) // BasicVendorExtender requires API level 23 58 class ImageCaptureConfigProviderTest { 59 @get:Rule 60 val useCamera = 61 CameraUtil.grantCameraPermissionAndPreTestAndPostTest( 62 PreTestCameraIdList(Camera2Config.defaultConfig()) 63 ) 64 65 private val context = ApplicationProvider.getApplicationContext<Context>() 66 67 // Tests in this class majorly use mock objects to run the test. No matter which extension 68 // mode is use, it should not affect the test results. 69 @ExtensionMode.Mode private val extensionMode = ExtensionMode.NONE 70 private var cameraSelector = CameraSelector.Builder().build() 71 private val fakeLifecycleOwner = FakeLifecycleOwner() 72 73 private lateinit var cameraProvider: ProcessCameraProvider 74 75 @Before setUpnull76 fun setUp() { 77 assumeTrue(CameraUtil.deviceHasCamera()) 78 79 cameraProvider = ProcessCameraProvider.getInstance(context)[10000, TimeUnit.MILLISECONDS] 80 fakeLifecycleOwner.startAndResume() 81 } 82 83 @After <lambda>null84 fun cleanUp(): Unit = runBlocking { 85 if (::cameraProvider.isInitialized) { 86 cameraProvider.shutdownAsync()[10000, TimeUnit.MILLISECONDS] 87 } 88 } 89 90 @Test 91 @MediumTest 92 @Ignore("b/331617278") <lambda>null93 fun canSetSupportedResolutionsToConfigTest(): Unit = runBlocking { 94 assumeTrue(CameraUtil.deviceHasCamera()) 95 96 // getSupportedResolutions supported since version 1.1 97 val version = ExtensionVersion.getRuntimeVersion() 98 assumeTrue(version != null && version >= Version.VERSION_1_1) 99 100 val mockImageCaptureExtenderImpl = mock(ImageCaptureExtenderImpl::class.java) 101 102 Mockito.`when`(mockImageCaptureExtenderImpl.isExtensionAvailable(any(), any())) 103 .thenReturn(true) 104 105 val targetFormatResolutionsPairList = generateImageCaptureSupportedResolutions() 106 Mockito.`when`(mockImageCaptureExtenderImpl.supportedResolutions) 107 .thenReturn(targetFormatResolutionsPairList) 108 109 val vendorExtender = BasicVendorExtender(mockImageCaptureExtenderImpl, null) 110 val preview = createImageCaptureWithExtenderImpl(vendorExtender) 111 112 withContext(Dispatchers.Main) { 113 cameraProvider.bindToLifecycle(fakeLifecycleOwner, cameraSelector, preview) 114 } 115 116 val resultFormatResolutionsPairList = 117 (preview.currentConfig as ImageOutputConfig).supportedResolutions 118 119 // Checks the result and target pair lists are the same 120 for (resultPair in resultFormatResolutionsPairList) { 121 val firstTargetSizes = 122 targetFormatResolutionsPairList 123 .filter { it.first == resultPair.first } 124 .map { it.second } 125 .first() 126 127 Truth.assertThat(mutableListOf(resultPair.second)).containsExactly(firstTargetSizes) 128 } 129 } 130 createImageCaptureWithExtenderImplnull131 private suspend fun createImageCaptureWithExtenderImpl( 132 basicVendorExtender: BasicVendorExtender 133 ): ImageCapture { 134 withContext(Dispatchers.Main) { 135 val camera = cameraProvider.bindToLifecycle(fakeLifecycleOwner, cameraSelector) 136 basicVendorExtender.init(camera.cameraInfo) 137 } 138 return ImageCapture.Builder() 139 .also { 140 ImageCaptureConfigProvider(basicVendorExtender).apply { 141 updateBuilderConfig(it, basicVendorExtender) 142 } 143 } 144 .build() 145 } 146 generateImageCaptureSupportedResolutionsnull147 private fun generateImageCaptureSupportedResolutions(): List<Pair<Int, Array<Size>>> { 148 val formatResolutionsPairList = mutableListOf<Pair<Int, Array<Size>>>() 149 val cameraInfo = cameraProvider.availableCameraInfos[0] as CameraInfoInternal 150 val characteristics = cameraInfo.cameraCharacteristics as CameraCharacteristics 151 val map = characteristics[CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP] 152 153 // Retrieves originally supported resolutions from CameraCharacteristics for JPEG 154 // format to return. 155 map?.getOutputSizes(ImageFormat.JPEG)?.let { 156 formatResolutionsPairList.add(Pair.create(ImageFormat.JPEG, it)) 157 } 158 159 return formatResolutionsPairList 160 } 161 } 162