Home | History | Annotate | Download | only in lifecycle
      1 /*
      2  * Copyright (C) 2017 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.lifecycle;
     18 
     19 import androidx.annotation.MainThread;
     20 import androidx.annotation.NonNull;
     21 import androidx.annotation.Nullable;
     22 import androidx.arch.core.util.Function;
     23 
     24 /**
     25  * Transformation methods for {@link LiveData}.
     26  * <p>
     27  * These methods permit functional composition and delegation of {@link LiveData} instances. The
     28  * transformations are calculated lazily, and will run only when the returned {@link LiveData} is
     29  * observed. Lifecycle behavior is propagated from the input {@code source} {@link LiveData} to the
     30  * returned one.
     31  */
     32 @SuppressWarnings("WeakerAccess")
     33 public class Transformations {
     34 
     35     private Transformations() {
     36     }
     37 
     38     /**
     39      * Returns a {@code LiveData} mapped from the input {@code source} {@code LiveData} by applying
     40      * {@code mapFunction} to each value set on {@code source}.
     41      * <p>
     42      * This method is analogous to {@link io.reactivex.Observable#map}.
     43      * <p>
     44      * {@code transform} will be executed on the main thread.
     45      * <p>
     46      * Here is an example mapping a simple {@code User} struct in a {@code LiveData} to a
     47      * {@code LiveData} containing their full name as a {@code String}.
     48      *
     49      * <pre>
     50      * LiveData<User> userLiveData = ...;
     51      * LiveData<String> userFullNameLiveData =
     52      *     Transformations.map(
     53      *         userLiveData,
     54      *         user -> user.firstName + user.lastName);
     55      * });
     56      * </pre>
     57      *
     58      * @param source      the {@code LiveData} to map from
     59      * @param mapFunction a function to apply to each value set on {@code source} in order to set
     60      *                    it
     61      *                    on the output {@code LiveData}
     62      * @param <X>         the generic type parameter of {@code source}
     63      * @param <Y>         the generic type parameter of the returned {@code LiveData}
     64      * @return a LiveData mapped from {@code source} to type {@code <Y>} by applying
     65      * {@code mapFunction} to each value set.
     66      */
     67     @MainThread
     68     public static <X, Y> LiveData<Y> map(
     69             @NonNull LiveData<X> source,
     70             @NonNull final Function<X, Y> mapFunction) {
     71         final MediatorLiveData<Y> result = new MediatorLiveData<>();
     72         result.addSource(source, new Observer<X>() {
     73             @Override
     74             public void onChanged(@Nullable X x) {
     75                 result.setValue(mapFunction.apply(x));
     76             }
     77         });
     78         return result;
     79     }
     80 
     81     /**
     82      * Returns a {@code LiveData} mapped from the input {@code source} {@code LiveData} by applying
     83      * {@code switchMapFunction} to each value set on {@code source}.
     84      * <p>
     85      * The returned {@code LiveData} delegates to the most recent {@code LiveData} created by
     86      * calling {@code switchMapFunction} with the most recent value set to {@code source}, without
     87      * changing the reference. In this way, {@code switchMapFunction} can change the 'backing'
     88      * {@code LiveData} transparently to any observer registered to the {@code LiveData} returned
     89      * by {@code switchMap()}.
     90      * <p>
     91      * Note that when the backing {@code LiveData} is switched, no further values from the older
     92      * {@code LiveData} will be set to the output {@code LiveData}. In this way, the method is
     93      * analogous to {@link io.reactivex.Observable#switchMap}.
     94      * <p>
     95      * {@code switchMapFunction} will be executed on the main thread.
     96      * <p>
     97      * Here is an example class that holds a typed-in name of a user
     98      * {@code String} (such as from an {@code EditText}) in a {@link MutableLiveData} and
     99      * returns a {@code LiveData} containing a List of {@code User} objects for users that have
    100      * that name. It populates that {@code LiveData} by requerying a repository-pattern object
    101      * each time the typed name changes.
    102      * <p>
    103      * This {@code ViewModel} would permit the observing UI to update "live" as the user ID text
    104      * changes.
    105      *
    106      * <pre>
    107      * class UserViewModel extends AndroidViewModel {
    108      *     MutableLiveData<String> nameQueryLiveData = ...
    109      *
    110      *     LiveData<List<String>> getUsersWithNameLiveData() {
    111      *         return Transformations.switchMap(
    112      *             nameQueryLiveData,
    113      *                 name -> myDataSource.getUsersWithNameLiveData(name));
    114      *     }
    115      *
    116      *     void setNameQuery(String name) {
    117      *         this.nameQueryLiveData.setValue(name);
    118      *     }
    119      * }
    120      * </pre>
    121      *
    122      * @param source            the {@code LiveData} to map from
    123      * @param switchMapFunction a function to apply to each value set on {@code source} to create a
    124      *                          new delegate {@code LiveData} for the returned one
    125      * @param <X>               the generic type parameter of {@code source}
    126      * @param <Y>               the generic type parameter of the returned {@code LiveData}
    127      * @return a LiveData mapped from {@code source} to type {@code <Y>} by delegating
    128      * to the LiveData returned by applying {@code switchMapFunction} to each
    129      * value set
    130      */
    131     @MainThread
    132     public static <X, Y> LiveData<Y> switchMap(
    133             @NonNull LiveData<X> source,
    134             @NonNull final Function<X, LiveData<Y>> switchMapFunction) {
    135         final MediatorLiveData<Y> result = new MediatorLiveData<>();
    136         result.addSource(source, new Observer<X>() {
    137             LiveData<Y> mSource;
    138 
    139             @Override
    140             public void onChanged(@Nullable X x) {
    141                 LiveData<Y> newLiveData = switchMapFunction.apply(x);
    142                 if (mSource == newLiveData) {
    143                     return;
    144                 }
    145                 if (mSource != null) {
    146                     result.removeSource(mSource);
    147                 }
    148                 mSource = newLiveData;
    149                 if (mSource != null) {
    150                     result.addSource(mSource, new Observer<Y>() {
    151                         @Override
    152                         public void onChanged(@Nullable Y y) {
    153                             result.setValue(y);
    154                         }
    155                     });
    156                 }
    157             }
    158         });
    159         return result;
    160     }
    161 }
    162