1 /* <lambda>null2 * Copyright 2022 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.diagnose 18 19 import android.content.Context 20 import android.util.Log 21 import androidx.annotation.MainThread 22 import androidx.camera.core.ImageCapture 23 import androidx.camera.core.ImageCaptureException 24 import androidx.camera.core.impl.utils.Threads 25 import androidx.camera.view.CameraController.IMAGE_CAPTURE 26 import androidx.camera.view.LifecycleCameraController 27 import androidx.core.content.ContextCompat 28 import java.io.File 29 import kotlin.coroutines.resume 30 import kotlin.coroutines.resumeWithException 31 import kotlinx.coroutines.asCoroutineDispatcher 32 import kotlinx.coroutines.suspendCancellableCoroutine 33 import kotlinx.coroutines.withContext 34 35 /** 36 * Diagnosis task that utilizes ImageCapture use case 37 * 38 * TODO: unit tests for this task (have only tested in end-to-end) 39 */ 40 class ImageCaptureTask : DiagnosisTask("ImageCaptureTask") { 41 42 /** Collects image captured as JPEG in diagnosis report zip */ 43 @Override 44 override suspend fun runDiagnosisTask( 45 cameraController: LifecycleCameraController, 46 dataStore: DataStore, 47 context: Context 48 ) { 49 // write file/section header 50 dataStore.appendTitle(this.getTaskName()) 51 52 try { 53 withContext(ContextCompat.getMainExecutor(context).asCoroutineDispatcher()) { 54 captureImage(cameraController, dataStore, context) 55 } 56 ?.let { dataStore.flushTempFileToImageFile(it, "ImageCaptureTask") } 57 } catch (exception: ImageCaptureException) { 58 Log.d("ImageCaptureTask", "Failed to run ImageCaptureTask: ${exception.message}") 59 } 60 } 61 62 /** Runs ImageCapture use case and return image captured */ 63 @MainThread 64 suspend fun captureImage( 65 cameraController: LifecycleCameraController, 66 dataStore: DataStore, 67 context: Context 68 ): File? = suspendCancellableCoroutine { continuation -> 69 Threads.checkMainThread() 70 71 // enable ImageCapture use case 72 cameraController.setEnabledUseCases(IMAGE_CAPTURE) 73 dataStore.appendText( 74 "image capture enabled: " + "${cameraController.isImageCaptureEnabled}" 75 ) 76 77 val file = File(context.cacheDir, "temp.jpeg") 78 val outputOption = ImageCapture.OutputFileOptions.Builder(file).build() 79 val mainExecutor = ContextCompat.getMainExecutor(context) 80 81 cameraController.takePicture( 82 outputOption, 83 mainExecutor, 84 object : ImageCapture.OnImageSavedCallback { 85 override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) { 86 Log.d("ImageCaptureTask", "${outputFileResults.savedUri}") 87 continuation.resume(file) 88 } 89 90 override fun onError(exception: ImageCaptureException) { 91 continuation.resumeWithException(exception) 92 } 93 } 94 ) 95 } 96 } 97