1 /* <lambda>null2 * 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 package androidx.camera.core.impl.utils 18 19 import androidx.annotation.MainThread 20 import androidx.arch.core.util.Function 21 import androidx.camera.core.impl.utils.Threads.runOnMain 22 import androidx.lifecycle.LiveData 23 import androidx.lifecycle.MediatorLiveData 24 import androidx.lifecycle.Observer 25 26 public object LiveDataUtil { 27 /** 28 * Returns a [LiveData] mapped from the input [LiveData] by applying {@code mapFunction} to each 29 * value set on {@code source}. 30 * 31 * Similar to [androidx.lifecycle.Transformations.map], but it can ensure [getValue] returns 32 * mapped value without the need to have active observers. 33 */ 34 @MainThread 35 @JvmStatic 36 public fun <I, O> map(source: LiveData<I>, mapFunction: Function<I, O>): LiveData<O> { 37 val result = MappingRedirectableLiveData<I, O>(mapFunction.apply(source.value), mapFunction) 38 result.redirectTo(source) 39 return result 40 } 41 } 42 43 /** 44 * A [LiveData] which can be redirected to a source [LiveData] in order to use the source data as 45 * its own. 46 * 47 * This `LiveData` is considered as the destination `LiveData` while the source `LiveData` is the 48 * one this redirects to. Whenever source `LiveData` gets updated with a value, this `LiveData` is 49 * also updated with that value. 50 * 51 * @param T The type of both the source `LiveData` and this `LiveData`. 52 * @property initialValue The initial value that is returned if no redirection is set yet. 53 * @see MappingRedirectableLiveData 54 */ 55 public class RedirectableLiveData<T>(private val initialValue: T) : <lambda>null56 MappingRedirectableLiveData<T, T>(initialValue, { it }) 57 58 /** 59 * A [LiveData] which can be redirected to a source [LiveData] in order to map the source data as 60 * its own data. 61 * 62 * This `LiveData` is considered as the destination `LiveData` while the source `LiveData` is the 63 * one this redirects to. Whenever source `LiveData` gets updated with a value, this `LiveData` is 64 * also updated with a mapped equivalent of that value. 65 * 66 * @param I The type of the source `LiveData`. 67 * @param O The type of the destination `LiveData`. 68 * @property initialValue The initial value that is returned if no redirection is set yet. 69 * @property mapFunction The function that maps source data. 70 * @see redirectTo 71 */ 72 public open class MappingRedirectableLiveData<I, O>( 73 private val initialValue: O, 74 private val mapFunction: Function<I, O> 75 ) : MediatorLiveData<O>() { 76 private var liveDataSource: LiveData<I>? = null 77 78 /** 79 * Redirects to a source [LiveData] whose value is mapped to this `LiveData`. 80 * 81 * After this function is invoked, the data of this `LiveData` will depend on the source live 82 * data with [mapFunction] invoked. 83 * 84 * @param liveDataSource The source `LiveData`. 85 */ redirectTonull86 public fun redirectTo(liveDataSource: LiveData<I>) { 87 if (this.liveDataSource != null) { 88 super.removeSource(this.liveDataSource!!) 89 } 90 this.liveDataSource = liveDataSource 91 runOnMain { 92 // addSource should be invoked in main thread. 93 super.addSource(liveDataSource) { value: I -> this.value = mapFunction.apply(value) } 94 } 95 } 96 addSourcenull97 override fun <S> addSource(source: LiveData<S>, onChanged: Observer<in S>) { 98 throw UnsupportedOperationException() 99 } 100 101 // Overrides getValue() to reflect the correct value from source. This is required to ensure 102 // getValue() is correct when observe() or observeForever() is not called. 103 /** 104 * Gets the value of this [LiveData] based on the provided redirection and mapping. 105 * 106 * If no redirection has been set yet, the [initialValue] property is returned. 107 * 108 * @see redirectTo 109 * @see MediatorLiveData.getValue 110 */ getValuenull111 override fun getValue(): O? { 112 // Returns initial value if source is not set. 113 val liveDataSource = this.liveDataSource // snapshot for non-null smart casting 114 if (liveDataSource == null) { 115 return initialValue 116 } 117 return mapFunction.apply(liveDataSource.value) 118 } 119 } 120