1 /* 2 * Copyright 2024 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 @file:Suppress("NOTHING_TO_INLINE") 18 19 package androidx.camera.camera2.pipe.internal 20 21 import androidx.camera.camera2.pipe.OutputStatus 22 import kotlinx.coroutines.CompletableDeferred 23 import kotlinx.coroutines.Deferred 24 import kotlinx.coroutines.ExperimentalCoroutinesApi 25 26 /** 27 * Inline value class that can be used in place of `Any` to represent either a valid output object 28 * OR an [OutputStatus] indicating why the result is not available. 29 */ 30 @JvmInline 31 internal value class OutputResult<out T> private constructor(internal val result: Any?) { 32 /** Returns `true` if this instance represents a successful outcome. */ 33 val available: Boolean 34 get() = !failure && result != null 35 36 /** Returns `true` if this instance represents a failed result. */ 37 val failure: Boolean 38 get() = result is OutputStatus 39 40 /** Returns the value, if [from], else null. */ 41 @Suppress("UNCHECKED_CAST") 42 inline val output: T? 43 get() = 44 when { 45 available -> result as T 46 else -> null 47 } 48 49 /** 50 * If this OutputResult represents a failure, then return the [OutputStatus] associated with it, 51 * otherwise report [OutputStatus.AVAILABLE] for successfully cases. 52 */ 53 inline val status: OutputStatus 54 get() = 55 when { 56 available -> OutputStatus.AVAILABLE 57 result == null -> OutputStatus.UNAVAILABLE 58 else -> result as OutputStatus 59 } 60 61 @OptIn(ExperimentalCoroutinesApi::class) 62 companion object { 63 /** Returns an instance that encapsulates the given [output] as successful value. */ fromnull64 inline fun <T> from(output: T): OutputResult<T> = OutputResult(output as Any?) 65 66 /** Returns an instance that encapsulates the given OutputStatus as a failure. */ 67 inline fun <T> failure(failureReason: OutputStatus): OutputResult<T> = 68 OutputResult(failureReason) 69 70 /** Utility function to complete a CompletableDeferred with a successful [OutputResult]. */ 71 inline fun <T> CompletableDeferred<OutputResult<T>>.completeWithOutput(output: T): Boolean { 72 return complete(from(output)) 73 } 74 75 /** Utility function to complete a CompletableDeferred with a [OutputStatus] failure */ completeWithFailurenull76 inline fun <T> CompletableDeferred<OutputResult<T>>.completeWithFailure( 77 status: OutputStatus 78 ): Boolean { 79 return complete(failure(status)) 80 } 81 82 /** 83 * For a [Deferred] object that contains an OutputResult, determine the status based on the 84 * state of the [Deferred] or from the actual object if this status has been completed. 85 */ outputStatusnull86 inline fun <T> Deferred<OutputResult<T>>.outputStatus(): OutputStatus { 87 return if (!isCompleted) { 88 // If the result is not completed, then this Output is in a PENDING state. 89 OutputStatus.PENDING 90 } else if (isCancelled) { 91 // If the result was canceled for any reason, then this Output is, and will not, be 92 // available. 93 OutputStatus.UNAVAILABLE 94 } else { 95 // If we reach here, the result is A) completed, and B) not canceled. read the 96 // status from the result and return it. 97 getCompleted().status 98 } 99 } 100 101 /** Get the output from this [Deferred], if available, or null. */ outputOrNullnull102 inline fun <T> Deferred<OutputResult<T>>.outputOrNull(): T? { 103 if (isCompleted && !isCancelled) { 104 return getCompleted().output 105 } 106 return null 107 } 108 } 109 } 110