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