1 package com.airbnb.lottie.compose 2 3 import androidx.compose.runtime.Stable 4 import androidx.compose.runtime.State 5 import androidx.compose.runtime.derivedStateOf 6 import androidx.compose.runtime.getValue 7 import androidx.compose.runtime.mutableStateOf 8 import androidx.compose.runtime.setValue 9 import com.airbnb.lottie.LottieComposition 10 import kotlinx.coroutines.CompletableDeferred 11 12 /** 13 * A [LottieCompositionResult] subclass is returned from [rememberLottieComposition]. 14 * It can be completed with a result or exception only one time. 15 * 16 * This class implements State<LottieComposition> so you either use it like: 17 * ``` 18 * val compositionResult = rememberLottieComposition(...) 19 * // Or 20 * val composition by rememberLottieComposition(...) 21 * ``` 22 * 23 * Use the former if you need to explicitly differentiate between loading and error states 24 * or if you need to call [await] or [awaitOrNull] in a coroutine such as [androidx.compose.runtime.LaunchedEffect]. 25 * 26 * @see rememberLottieComposition 27 * @see LottieAnimation 28 */ 29 @Stable 30 interface LottieCompositionResult : State<LottieComposition?> { 31 /** 32 * The composition or null if it hasn't yet loaded or failed to load. 33 */ 34 override val value: LottieComposition? 35 36 /** 37 * The exception that was thrown while trying to load and parse the composition. 38 */ 39 val error: Throwable? 40 41 /** 42 * Whether or not the composition is still being loaded and parsed. 43 */ 44 val isLoading: Boolean 45 46 /** 47 * Whether or not the composition is in the process of being loaded or parsed. 48 */ 49 val isComplete: Boolean 50 51 /** 52 * Whether or not the composition failed to load. This is terminal. It only occurs after 53 * returning false from [rememberLottieComposition]'s onRetry lambda. 54 */ 55 val isFailure: Boolean 56 57 /** 58 * Whether or not the composition has succeeded yet. 59 */ 60 val isSuccess: Boolean 61 62 /** 63 * Suspend until the composition has finished parsing. 64 * 65 * This can throw if the [LottieComposition] fails to load. 66 * 67 * These animations should never fail given a valid input: 68 * * [LottieCompositionSpec.RawRes] 69 * * [LottieCompositionSpec.Asset] 70 * * [LottieCompositionSpec.JsonString] 71 * 72 * These animations may fail: 73 * * [LottieCompositionSpec.Url] 74 * * [LottieCompositionSpec.File] 75 */ awaitnull76 suspend fun await(): LottieComposition 77 } 78 79 /** 80 * Like [LottieCompositionResult.await] but returns null instead of throwing an exception if the animation fails 81 * to load. 82 */ 83 suspend fun LottieCompositionResult.awaitOrNull(): LottieComposition? { 84 return try { 85 await() 86 } catch (e: Throwable) { 87 null 88 } 89 } 90 91 @Stable 92 internal class LottieCompositionResultImpl : LottieCompositionResult { 93 private val compositionDeferred = CompletableDeferred<LottieComposition>() 94 95 override var value: LottieComposition? by mutableStateOf(null) 96 private set 97 98 override var error by mutableStateOf<Throwable?>(null) 99 private set 100 <lambda>null101 override val isLoading by derivedStateOf { value == null && error == null } 102 <lambda>null103 override val isComplete by derivedStateOf { value != null || error != null } 104 <lambda>null105 override val isFailure by derivedStateOf { error != null } 106 <lambda>null107 override val isSuccess by derivedStateOf { value != null } 108 awaitnull109 override suspend fun await(): LottieComposition { 110 return compositionDeferred.await() 111 } 112 113 @Synchronized completenull114 internal fun complete(composition: LottieComposition) { 115 if (isComplete) return 116 117 this.value = composition 118 compositionDeferred.complete(composition) 119 } 120 121 @Synchronized completeExceptionallynull122 internal fun completeExceptionally(error: Throwable) { 123 if (isComplete) return 124 125 this.error = error 126 compositionDeferred.completeExceptionally(error) 127 } 128 }