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