1 /* <lambda>null2 * Copyright 2020 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.integration.uiwidgets.viewpager 18 19 import android.content.Context 20 import android.content.Intent 21 import android.hardware.camera2.CameraCaptureSession 22 import android.hardware.camera2.CaptureRequest 23 import android.hardware.camera2.TotalCaptureResult 24 import android.os.Bundle 25 import android.text.TextUtils 26 import android.util.Log 27 import android.view.LayoutInflater 28 import android.view.View 29 import android.view.ViewGroup 30 import androidx.annotation.OptIn 31 import androidx.annotation.VisibleForTesting 32 import androidx.camera.camera2.Camera2Config 33 import androidx.camera.camera2.interop.Camera2Interop 34 import androidx.camera.camera2.interop.ExperimentalCamera2Interop 35 import androidx.camera.camera2.pipe.integration.CameraPipeConfig 36 import androidx.camera.core.CameraSelector 37 import androidx.camera.core.Preview 38 import androidx.camera.integration.uiwidgets.databinding.FragmentTextureviewBinding 39 import androidx.camera.integration.uiwidgets.viewpager.BaseActivity.Companion.COMPATIBLE_MODE 40 import androidx.camera.integration.uiwidgets.viewpager.BaseActivity.Companion.PERFORMANCE_MODE 41 import androidx.camera.lifecycle.ExperimentalCameraProviderConfiguration 42 import androidx.camera.lifecycle.ProcessCameraProvider 43 import androidx.camera.view.PreviewView.ImplementationMode 44 import androidx.core.content.ContextCompat 45 import androidx.fragment.app.Fragment 46 import androidx.lifecycle.Lifecycle 47 import com.google.common.util.concurrent.ListenableFuture 48 import java.util.concurrent.CountDownLatch 49 50 /** A Fragment that displays a {@link PreviewView} with TextureView mode. */ 51 class CameraFragment : Fragment() { 52 53 companion object { 54 fun newInstance() = CameraFragment() 55 56 private const val TAG = "CameraFragment" 57 const val KEY_CAMERA_IMPLEMENTATION = "camera_implementation" 58 const val KEY_CAMERA_IMPLEMENTATION_NO_HISTORY = "camera_implementation_no_history" 59 const val CAMERA2_IMPLEMENTATION_OPTION = "camera2" 60 const val CAMERA_PIPE_IMPLEMENTATION_OPTION = "camera_pipe" 61 private var cameraImpl: String? = null 62 } 63 64 private var _binding: FragmentTextureviewBinding? = null 65 private val binding 66 get() = _binding!! 67 68 private lateinit var cameraProviderFuture: ListenableFuture<ProcessCameraProvider> 69 private lateinit var cameraProvider: ProcessCameraProvider 70 71 // for testing preview updates 72 private var previewUpdatingLatch: CountDownLatch? = null 73 74 @OptIn(ExperimentalCameraProviderConfiguration::class) 75 override fun onAttach(context: Context) { 76 super.onAttach(context) 77 val newCameraImpl = activity?.intent?.getStringExtra(KEY_CAMERA_IMPLEMENTATION) 78 Log.d(TAG, "Set up cameraImpl: $newCameraImpl") 79 if (!TextUtils.isEmpty(newCameraImpl) && newCameraImpl != cameraImpl) { 80 try { 81 Log.d(TAG, "ProcessCameraProvider initialize using $newCameraImpl") 82 ProcessCameraProvider.configureInstance( 83 when (newCameraImpl) { 84 CAMERA2_IMPLEMENTATION_OPTION -> Camera2Config.defaultConfig() 85 CAMERA_PIPE_IMPLEMENTATION_OPTION -> CameraPipeConfig.defaultConfig() 86 else -> Camera2Config.defaultConfig() 87 } 88 ) 89 cameraImpl = newCameraImpl 90 } catch (e: IllegalStateException) { 91 throw IllegalStateException( 92 "WARNING: CameraX is currently configured to a different implementation " + 93 "this would have resulted in unexpected behavior.", 94 e 95 ) 96 } 97 } 98 99 activity?.intent?.let { intent -> 100 if (intent.getBooleanExtra(KEY_CAMERA_IMPLEMENTATION_NO_HISTORY, false)) { 101 activity?.intent = 102 Intent(intent).apply { 103 removeExtra(KEY_CAMERA_IMPLEMENTATION) 104 removeExtra(KEY_CAMERA_IMPLEMENTATION_NO_HISTORY) 105 } 106 cameraImpl = null 107 } 108 } 109 110 cameraProviderFuture = ProcessCameraProvider.getInstance(context) 111 } 112 113 override fun onCreateView( 114 inflater: LayoutInflater, 115 container: ViewGroup?, 116 savedInstanceState: Bundle? 117 ): View { 118 _binding = FragmentTextureviewBinding.inflate(inflater, container, false) 119 return binding.root 120 } 121 122 override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 123 Log.d(TAG, "onViewCreated") 124 (requireActivity() as BaseActivity).previewView = binding.previewTextureview 125 126 cameraProviderFuture.addListener( 127 Runnable { 128 cameraProvider = cameraProviderFuture.get() 129 val lifecycleOwner = viewLifecycleOwnerLiveData.value 130 if ( 131 lifecycleOwner != null && 132 lifecycleOwner.lifecycle.currentState != Lifecycle.State.DESTROYED 133 ) { 134 bindPreview() 135 } else { 136 Log.d(TAG, "Skip camera setup since the lifecycle is closed") 137 } 138 }, 139 ContextCompat.getMainExecutor(requireContext()) 140 ) 141 } 142 143 override fun onDestroyView() { 144 super.onDestroyView() 145 _binding = null 146 } 147 148 private fun bindPreview() { 149 Log.d(TAG, "bindPreview") 150 151 val previewBuilder = Preview.Builder() 152 previewBuilder.addCaptureCompletedCallback() 153 val preview = previewBuilder.setTargetName("Preview").build() 154 155 cameraProvider.bindToLifecycle(this, getCameraSelector(), preview) 156 157 binding.previewTextureview.implementationMode = getImplementationMode() 158 preview.setSurfaceProvider(binding.previewTextureview.surfaceProvider) 159 } 160 161 private fun getCameraSelector(): CameraSelector { 162 val lensFacing = 163 (requireActivity() as BaseActivity) 164 .intent 165 .getIntExtra(BaseActivity.INTENT_LENS_FACING, CameraSelector.LENS_FACING_BACK) 166 return CameraSelector.Builder().requireLensFacing(lensFacing).build() 167 } 168 169 /** 170 * Returns the implementation mode from the intent, or return the compatibility mode if not set. 171 */ 172 private fun getImplementationMode(): ImplementationMode { 173 val mode = 174 (requireActivity() as BaseActivity) 175 .intent 176 .getIntExtra(BaseActivity.INTENT_IMPLEMENTATION_MODE, COMPATIBLE_MODE) 177 178 return when (mode) { 179 PERFORMANCE_MODE -> ImplementationMode.PERFORMANCE 180 else -> ImplementationMode.COMPATIBLE 181 } 182 } 183 184 /** 185 * Implements preview updating latch with interop to workaround the situation that SurfaceView's 186 * content can not be got. 187 */ 188 @OptIn(ExperimentalCamera2Interop::class) 189 @kotlin.OptIn( 190 androidx.camera.camera2.pipe.integration.interop.ExperimentalCamera2Interop::class 191 ) 192 private fun Preview.Builder.addCaptureCompletedCallback() { 193 val captureCallback = 194 object : CameraCaptureSession.CaptureCallback() { 195 override fun onCaptureCompleted( 196 session: CameraCaptureSession, 197 request: CaptureRequest, 198 result: TotalCaptureResult 199 ) { 200 super.onCaptureCompleted(session, request, result) 201 202 if (previewUpdatingLatch != null) { 203 previewUpdatingLatch!!.countDown() 204 } 205 } 206 } 207 208 if (cameraImpl.equals(CAMERA_PIPE_IMPLEMENTATION_OPTION)) { 209 androidx.camera.camera2.pipe.integration.interop.Camera2Interop.Extender(this) 210 .setSessionCaptureCallback(captureCallback) 211 } else { 212 Camera2Interop.Extender(this).setSessionCaptureCallback(captureCallback) 213 } 214 Camera2Interop.Extender(this).setSessionCaptureCallback(captureCallback) 215 } 216 217 @VisibleForTesting 218 fun setPreviewUpdatingLatch(latch: CountDownLatch) { 219 previewUpdatingLatch = latch 220 } 221 } 222