1 /*
2  * Copyright (C) 2021 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 com.android.car.apps.common.util;
18 
19 import static java.util.Objects.requireNonNull;
20 
21 import androidx.annotation.NonNull;
22 import androidx.annotation.Nullable;
23 import androidx.arch.core.util.Function;
24 import androidx.core.util.Pair;
25 import androidx.lifecycle.LiveData;
26 import androidx.lifecycle.MediatorLiveData;
27 import androidx.lifecycle.MutableLiveData;
28 import androidx.lifecycle.Transformations;
29 
30 import java.util.function.BiFunction;
31 import java.util.function.Predicate;
32 
33 /**
34  * Utility methods for using {@link LiveData}. In general for Boolean operations, {@code null} is
35  * treated as an "unknown" value, and operations may use short-circuit evaluation to determine the
36  * result. LiveData may be in an uninitialized state where observers are not called when registered
37  * (e.g. a {@link MutableLiveData} where {@link MutableLiveData#setValue(Object)} has not yet been
38  * called). If a Boolean operation receives an uninitialized LiveData as either of its parameters,
39  * the result will also be in an uninitialized state.
40  */
41 @SuppressWarnings({"unused", "WeakerAccess"})
42 public class LiveDataFunctions {
43 
LiveDataFunctions()44     private LiveDataFunctions() {
45     }
46 
47     private static volatile LiveData<?> sNullLiveData;
48     private static volatile LiveData<Boolean> sTrueLiveData;
49     private static volatile LiveData<Boolean> sFalseLiveData;
50 
51     /**
52      * Returns a LiveData that always emits {@code null}. This is different than an uninitialized
53      * LiveData in that observers will be called (with {@code null}) when registered.
54      */
nullLiveData()55     public static <T> LiveData<T> nullLiveData() {
56         if (sNullLiveData == null) {
57             sNullLiveData = dataOf(null);
58         }
59         // null can fit any generic type
60         // noinspection unchecked
61         return (LiveData<T>) sNullLiveData;
62     }
63 
64     /** Returns a LiveData that is initialized with {@code value}. */
dataOf(@ullable T value)65     public static <T> MutableLiveData<T> dataOf(@Nullable T value) {
66         MutableLiveData<T> data = new MutableLiveData<>();
67         data.setValue(value);
68         return data;
69     }
70 
71     /**
72      * Similar to {@link Transformations#map(LiveData, Function)}, but emits {@code null} when
73      * {@code source} emits {@code null}. The input to {@code func} may be treated as not nullable.
74      */
mapNonNull(@onNull LiveData<T> source, @NonNull Function<T, R> func)75     public static <T, R> LiveData<R> mapNonNull(@NonNull LiveData<T> source,
76             @NonNull Function<T, R> func) {
77         return mapNonNull(source, null, func);
78     }
79 
80     /**
81      * Similar to {@link Transformations#map(LiveData, Function)}, but emits {@code nullValue} when
82      * {@code source} emits {@code null}. The input to {@code func} may be treated as not nullable.
83      */
mapNonNull(@onNull LiveData<T> source, @Nullable R nullValue, @NonNull Function<T, R> func)84     public static <T, R> LiveData<R> mapNonNull(@NonNull LiveData<T> source, @Nullable R nullValue,
85             @NonNull Function<T, R> func) {
86         return Transformations.map(source, value -> value == null ? nullValue : func.apply(value));
87     }
88 
89     /**
90      * Similar to {@link Transformations#switchMap(LiveData, Function)}, but emits {@code null} when
91      * {@code source} emits {@code null}. The input to {@code func} may be treated as not nullable.
92      */
switchMapNonNull(@onNull LiveData<T> source, @NonNull Function<T, LiveData<R>> func)93     public static <T, R> LiveData<R> switchMapNonNull(@NonNull LiveData<T> source,
94             @NonNull Function<T, LiveData<R>> func) {
95         return switchMapNonNull(source, null, func);
96     }
97 
98     /**
99      * Similar to {@link Transformations#switchMap(LiveData, Function)}, but emits {@code nullValue}
100      * when {@code source} emits {@code null}. The input to {@code func} may be treated as not
101      * nullable.
102      */
switchMapNonNull(@onNull LiveData<T> source, @Nullable R nullValue, @NonNull Function<T, LiveData<R>> func)103     public static <T, R> LiveData<R> switchMapNonNull(@NonNull LiveData<T> source,
104             @Nullable R nullValue,
105             @NonNull Function<T, LiveData<R>> func) {
106         return Transformations.switchMap(source,
107                 value -> value == null ? nullLiveData() : func.apply(value));
108     }
109 
110     /**
111      * Similar to {@link Transformations#switchMap(LiveData, Function)}, but emits a FutureData,
112      * which provides a loading field for operations which may take a long time to finish.
113      *
114      * This LiveData emits values only when the loading status of the output changes. It will never
115      * emit {@code null}. If the output is loading, the emitted FutureData will have a null value
116      * for the data.
117      */
loadingSwitchMap(LiveData<T> trigger, @NonNull Function<T, LiveData<R>> func)118     public static <T, R> LiveData<FutureData<R>> loadingSwitchMap(LiveData<T> trigger,
119             @NonNull Function<T, LiveData<R>> func) {
120         LiveData<R> output = Transformations.switchMap(trigger, func);
121         return new MediatorLiveData<FutureData<R>>() {
122             {
123                 addSource(trigger, data -> setValue(new FutureData<>(true, null)));
124                 addSource(output, data ->
125                         setValue(new FutureData<>(false, output.getValue())));
126             }
127         };
128     }
129 
130     /**
131      * Returns a LiveData backed by {@code value} if and only if predicate emits {@code true}. Emits
132      * {@code null} otherwise.
133      * <p>
134      * This is equivalent to {@code iff(predicate, Boolean::booleanValue, value)}
135      *
136      * @see #iff(LiveData, Predicate, LiveData)
137      */
138     public static <T> LiveData<T> iff(
139             @NonNull LiveData<Boolean> predicate, @NonNull LiveData<T> value) {
140         return iff(predicate, Boolean::booleanValue, value);
141     }
142 
143     /**
144      * Returns a LiveData backed by {@code value} if and only if the trigger emits a value that
145      * causes {@code predicate} to return {@code true}. Emits {@code null} otherwise.
146      */
147     public static <P, T> LiveData<T> iff(
148             @NonNull LiveData<P> trigger,
149             @NonNull Predicate<? super P> predicate,
150             @NonNull LiveData<T> value) {
151         return new BinaryOperation<>(
152                 trigger, value, (p, v) -> p == null || !predicate.test(p) ? null : v);
153     }
154 
155     /**
156      * Returns a LiveData that emits a Pair containing the values of the two parameter LiveDatas. If
157      * either parameter is uninitialized, the resulting LiveData is also uninitialized.
158      * <p>
159      * This is equivalent to calling {@code combine(tData, uData, Pair::new)}.
160      *
161      * @see #combine(LiveData, LiveData, BiFunction)
162      */
163     public static <T, U> LiveData<Pair<T, U>> pair(
164             @NonNull LiveData<T> tData, @NonNull LiveData<U> uData) {
165         return combine(tData, uData, Pair::new);
166     }
167 
168     /**
169      * Returns a LiveData that emits the result of {@code function} on the values of the two
170      * parameter LiveDatas. If either parameter is uninitialized, the resulting LiveData is also
171      * uninitialized.
172      */
173     public static <T, U, R> LiveData<R> combine(
174             @NonNull LiveData<T> tData,
175             @NonNull LiveData<U> uData,
176             @NonNull BiFunction<T, U, R> function) {
177         return new BinaryOperation<>(tData, uData, function);
178     }
179 
180     private static class BinaryOperation<T, U, R> extends MediatorLiveData<R> {
181         @NonNull
182         private final BiFunction<T, U, R> mFunction;
183 
184         private boolean mTSet;
185         private boolean mUSet;
186         private boolean mValueSet;
187 
188         @Nullable
189         private T mTValue;
190         @Nullable
191         private U mUValue;
192 
193         BinaryOperation(
194                 @NonNull LiveData<T> tLiveData,
195                 @NonNull LiveData<U> uLiveData,
196                 @NonNull BiFunction<T, U, R> function) {
197             this(tLiveData, uLiveData, true, true, function);
198         }
199 
200         BinaryOperation(
201                 @NonNull LiveData<T> tLiveData,
202                 @NonNull LiveData<U> uLiveData,
203                 boolean requireTSet,
204                 boolean requireUSet,
205                 @NonNull BiFunction<T, U, R> function) {
206             this.mFunction = function;
207             if (!requireTSet) {
208                 mTSet = true;
209             }
210             if (!requireUSet) {
211                 mUSet = true;
212             }
213             if (tLiveData == uLiveData) {
214                 // Only add the source once and only update once when it changes.
215                 addSource(
216                         tLiveData,
217                         value -> {
218                             mTSet = true;
219                             mUSet = true;
220                             mTValue = value;
221                             // if both references point to the same LiveData, then T and U are
222                             // compatible types.
223                             // noinspection unchecked
224                             mUValue = (U) value;
225                             update();
226                         });
227             } else {
228                 addSource(requireNonNull(tLiveData), this::updateT);
229                 addSource(requireNonNull(uLiveData), this::updateU);
230             }
231         }
232 
233         private void updateT(@Nullable T tValue) {
234             mTSet = true;
235             this.mTValue = tValue;
236             update();
237         }
238 
239         private void updateU(@Nullable U uValue) {
240             mUSet = true;
241             this.mUValue = uValue;
242             update();
243         }
244 
245         private void update() {
246             if (mTSet && mUSet) {
247                 R result = mFunction.apply(mTValue, mUValue);
248                 // Don't setValue if it's the same as the old value unless we haven't set a value
249                 // before.
250                 if (!mValueSet || result != getValue()) {
251                     mValueSet = true;
252                     setValue(result);
253                 }
254             }
255         }
256     }
257 }
258