Home | History | Annotate | Download | only in navigation
      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.navigation;
     18 
     19 import android.app.Activity;
     20 import android.content.Intent;
     21 import android.os.Bundle;
     22 import android.support.annotation.AnimRes;
     23 import android.support.annotation.AnimatorRes;
     24 import android.support.annotation.IdRes;
     25 import android.support.annotation.NonNull;
     26 import android.support.annotation.Nullable;
     27 
     28 /**
     29  * NavOptions stores special options for navigate actions
     30  */
     31 public class NavOptions {
     32     static final int LAUNCH_SINGLE_TOP = 0x1;
     33     static final int LAUNCH_DOCUMENT = 0x2;
     34     static final int LAUNCH_CLEAR_TASK = 0x4;
     35 
     36     private static final String KEY_NAV_OPTIONS = "android-support-nav:navOptions";
     37     private static final String KEY_LAUNCH_MODE = "launchMode";
     38     private static final String KEY_POP_UP_TO = "popUpTo";
     39     private static final String KEY_POP_UP_TO_INCLUSIVE = "popUpToInclusive";
     40     private static final String KEY_ENTER_ANIM = "enterAnim";
     41     private static final String KEY_EXIT_ANIM = "exitAnim";
     42     private static final String KEY_POP_ENTER_ANIM = "popEnterAnim";
     43     private static final String KEY_POP_EXIT_ANIM = "popExitAnim";
     44 
     45     /**
     46      * Add the {@link #getPopEnterAnim() pop enter} and {@link #getPopExitAnim() pop exit}
     47      * animation to an Intent for later usage with
     48      * {@link #applyPopAnimationsToPendingTransition(Activity)}.
     49      * <p>
     50      * This is automatically called for you by {@link ActivityNavigator}.
     51      * </p>
     52      *
     53      * @param intent Intent being started with the given NavOptions
     54      * @param navOptions NavOptions containing the pop animations.
     55      * @see #applyPopAnimationsToPendingTransition(Activity)
     56      * @see #getPopEnterAnim()
     57      * @see #getPopExitAnim()
     58      */
     59     public static void addPopAnimationsToIntent(@NonNull Intent intent,
     60             @Nullable NavOptions navOptions) {
     61         if (navOptions != null) {
     62             intent.putExtra(KEY_NAV_OPTIONS, navOptions.toBundle());
     63         }
     64     }
     65 
     66     /**
     67      * Apply any pop animations in the Intent of the given Activity to a pending transition.
     68      * This should be used in place of  {@link Activity#overridePendingTransition(int, int)}
     69      * to get the appropriate pop animations.
     70      * @param activity An activity started from the {@link ActivityNavigator}.
     71      * @see #addPopAnimationsToIntent(Intent, NavOptions)
     72      * @see #getPopEnterAnim()
     73      * @see #getPopExitAnim()
     74      */
     75     public static void applyPopAnimationsToPendingTransition(@NonNull Activity activity) {
     76         Intent intent = activity.getIntent();
     77         if (intent == null) {
     78             return;
     79         }
     80         Bundle bundle = intent.getBundleExtra(KEY_NAV_OPTIONS);
     81         if (bundle != null) {
     82             NavOptions navOptions = NavOptions.fromBundle(bundle);
     83             int popEnterAnim = navOptions.getPopEnterAnim();
     84             int popExitAnim = navOptions.getPopExitAnim();
     85             if (popEnterAnim != -1 || popExitAnim != -1) {
     86                 popEnterAnim = popEnterAnim != -1 ? popEnterAnim : 0;
     87                 popExitAnim = popExitAnim != -1 ? popExitAnim : 0;
     88                 activity.overridePendingTransition(popEnterAnim, popExitAnim);
     89             }
     90         }
     91     }
     92 
     93     private int mLaunchMode;
     94     @IdRes
     95     private int mPopUpTo;
     96     private boolean mPopUpToInclusive;
     97     @AnimRes @AnimatorRes
     98     private int mEnterAnim;
     99     @AnimRes @AnimatorRes
    100     private int mExitAnim;
    101     @AnimRes @AnimatorRes
    102     private int mPopEnterAnim;
    103     @AnimRes @AnimatorRes
    104     private int mPopExitAnim;
    105 
    106     NavOptions(int launchMode, @IdRes int popUpTo, boolean popUpToInclusive,
    107             @AnimRes @AnimatorRes int enterAnim, @AnimRes @AnimatorRes int exitAnim,
    108             @AnimRes @AnimatorRes int popEnterAnim, @AnimRes @AnimatorRes int popExitAnim) {
    109         mLaunchMode = launchMode;
    110         mPopUpTo = popUpTo;
    111         mPopUpToInclusive = popUpToInclusive;
    112         mEnterAnim = enterAnim;
    113         mExitAnim = exitAnim;
    114         mPopEnterAnim = popEnterAnim;
    115         mPopExitAnim = popExitAnim;
    116     }
    117 
    118     /**
    119      * Whether this navigation action should launch as single-top (i.e., there will be at most
    120      * one copy of a given destination on the top of the back stack).
    121      * <p>
    122      * This functions similarly to how {@link android.content.Intent#FLAG_ACTIVITY_SINGLE_TOP}
    123      * works with activites.
    124      */
    125     public boolean shouldLaunchSingleTop() {
    126         return (mLaunchMode & LAUNCH_SINGLE_TOP) != 0;
    127     }
    128 
    129     /**
    130      * Whether this navigation action should launch the destination in a new document.
    131      * <p>
    132      * This functions similarly to how {@link android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT}
    133      * works with activites.
    134      * @deprecated As per the {@link android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT}
    135      * documentation, it is recommended to use {@link android.R.attr#documentLaunchMode} on an
    136      * Activity you wish to launch as a new document.
    137      */
    138     @Deprecated
    139     public boolean shouldLaunchDocument() {
    140         return (mLaunchMode & LAUNCH_DOCUMENT) != 0;
    141     }
    142 
    143     /**
    144      * Whether this navigation action should clear the entire back stack
    145      * <p>
    146      * This functions similarly to how {@link android.content.Intent#FLAG_ACTIVITY_CLEAR_TASK}
    147      * works with activites.
    148      * @deprecated This is synonymous with {@link #getPopUpTo()} with the root of the graph and
    149      * using {@link #isPopUpToInclusive()}.
    150      */
    151     @Deprecated
    152     public boolean shouldClearTask() {
    153         return (mLaunchMode & LAUNCH_CLEAR_TASK) != 0;
    154     }
    155 
    156     /**
    157      * The destination to pop up to before navigating. When set, all non-matching destinations
    158      * should be popped from the back stack.
    159      * @return the destinationId to pop up to, clearing all intervening destinations
    160      * @see Builder#setPopUpTo
    161      * @see #isPopUpToInclusive
    162      */
    163     @IdRes
    164     public int getPopUpTo() {
    165         return mPopUpTo;
    166     }
    167 
    168     /**
    169      * Whether the destination set in {@link #getPopUpTo} should be popped from the back stack.
    170      * @see Builder#setPopUpTo
    171      * @see #getPopUpTo
    172      */
    173     public boolean isPopUpToInclusive() {
    174         return mPopUpToInclusive;
    175     }
    176 
    177     /**
    178      * The custom enter Animation/Animator that should be run.
    179      * @return the resource id of a Animation or Animator or -1 if none.
    180      */
    181     @AnimRes @AnimatorRes
    182     public int getEnterAnim() {
    183         return mEnterAnim;
    184     }
    185 
    186     /**
    187      * The custom exit Animation/Animator that should be run.
    188      * @return the resource id of a Animation or Animator or -1 if none.
    189      */
    190     @AnimRes @AnimatorRes
    191     public int getExitAnim() {
    192         return mExitAnim;
    193     }
    194 
    195     /**
    196      * The custom enter Animation/Animator that should be run when this destination is
    197      * popped from the back stack.
    198      * @return the resource id of a Animation or Animator or -1 if none.
    199      * @see #applyPopAnimationsToPendingTransition(Activity)
    200      */
    201     @AnimRes @AnimatorRes
    202     public int getPopEnterAnim() {
    203         return mPopEnterAnim;
    204     }
    205 
    206     /**
    207      * The custom exit Animation/Animator that should be run when this destination is
    208      * popped from the back stack.
    209      * @return the resource id of a Animation or Animator or -1 if none.
    210      * @see #applyPopAnimationsToPendingTransition(Activity)
    211      */
    212     @AnimRes @AnimatorRes
    213     public int getPopExitAnim() {
    214         return mPopExitAnim;
    215     }
    216 
    217     @NonNull
    218     private Bundle toBundle() {
    219         Bundle b = new Bundle();
    220         b.putInt(KEY_LAUNCH_MODE, mLaunchMode);
    221         b.putInt(KEY_POP_UP_TO, mPopUpTo);
    222         b.putBoolean(KEY_POP_UP_TO_INCLUSIVE, mPopUpToInclusive);
    223         b.putInt(KEY_ENTER_ANIM, mEnterAnim);
    224         b.putInt(KEY_EXIT_ANIM, mExitAnim);
    225         b.putInt(KEY_POP_ENTER_ANIM, mPopEnterAnim);
    226         b.putInt(KEY_POP_EXIT_ANIM, mPopExitAnim);
    227         return b;
    228     }
    229 
    230     @NonNull
    231     private static NavOptions fromBundle(@NonNull Bundle b) {
    232         return new NavOptions(b.getInt(KEY_LAUNCH_MODE, 0),
    233                 b.getInt(KEY_POP_UP_TO, 0), b.getBoolean(KEY_POP_UP_TO_INCLUSIVE, false),
    234                 b.getInt(KEY_ENTER_ANIM, -1), b.getInt(KEY_EXIT_ANIM, -1),
    235                 b.getInt(KEY_POP_ENTER_ANIM, -1), b.getInt(KEY_POP_EXIT_ANIM, -1));
    236     }
    237 
    238     /**
    239      * Builder for constructing new instances of NavOptions.
    240      */
    241     public static class Builder {
    242         int mLaunchMode;
    243         @IdRes
    244         int mPopUpTo;
    245         boolean mPopUpToInclusive;
    246         @AnimRes @AnimatorRes
    247         int mEnterAnim = -1;
    248         @AnimRes @AnimatorRes
    249         int mExitAnim = -1;
    250         @AnimRes @AnimatorRes
    251         int mPopEnterAnim = -1;
    252         @AnimRes @AnimatorRes
    253         int mPopExitAnim = -1;
    254 
    255         public Builder() {
    256         }
    257 
    258         /**
    259          * Launch a navigation target as single-top if you are making a lateral navigation
    260          * between instances of the same target (e.g. detail pages about similar data items)
    261          * that should not preserve history.
    262          *
    263          * @param singleTop true to launch as single-top
    264          */
    265         @NonNull
    266         public Builder setLaunchSingleTop(boolean singleTop) {
    267             if (singleTop) {
    268                 mLaunchMode |= LAUNCH_SINGLE_TOP;
    269             } else {
    270                 mLaunchMode &= ~LAUNCH_SINGLE_TOP;
    271             }
    272             return this;
    273         }
    274 
    275         /**
    276          * Launch a navigation target as a document if you want it to appear as its own
    277          * entry in the system Overview screen. If the same document is launched multiple times
    278          * it will not create a new task, it will bring the existing document task to the front.
    279          *
    280          * <p>If the user presses the system Back key from a new document task they will land
    281          * on their previous task. If the user reached the document task from the system Overview
    282          * screen they will be taken to their home screen.</p>
    283          *
    284          * @param launchDocument true to launch a new document task
    285          * @deprecated As per the {@link android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT}
    286          * documentation, it is recommended to use {@link android.R.attr#documentLaunchMode} on an
    287          * Activity you wish to launch as a new document.
    288          */
    289         @Deprecated
    290         @NonNull
    291         public Builder setLaunchDocument(boolean launchDocument) {
    292             if (launchDocument) {
    293                 mLaunchMode |= LAUNCH_DOCUMENT;
    294             } else {
    295                 mLaunchMode &= ~LAUNCH_DOCUMENT;
    296             }
    297             return this;
    298         }
    299 
    300         /**
    301          * Clear the entire task before launching this target. If you are launching as a
    302          * {@link #setLaunchDocument(boolean) document}, this will clear the document task.
    303          * Otherwise it will clear the current task.
    304          *
    305          * @param clearTask
    306          * @return
    307          * @deprecated Use {@link #setPopUpTo(int, boolean)} with the
    308          * {@link NavDestination#getId() id} of the
    309          * {@link androidx.navigation.NavController#getGraph() NavController's graph}
    310          * and set inclusive to true.
    311          */
    312         @Deprecated
    313         @NonNull
    314         public Builder setClearTask(boolean clearTask) {
    315             if (clearTask) {
    316                 mLaunchMode |= LAUNCH_CLEAR_TASK;
    317             } else {
    318                 mLaunchMode &= ~LAUNCH_CLEAR_TASK;
    319             }
    320             return this;
    321         }
    322 
    323         /**
    324          * Pop up to a given destination before navigating. This pops all non-matching destinations
    325          * from the back stack until this destination is found.
    326          *
    327          * @param destinationId The destination to pop up to, clearing all intervening destinations.
    328          * @param inclusive true to also pop the given destination from the back stack.
    329          * @return this Builder
    330          * @see NavOptions#getPopUpTo
    331          * @see NavOptions#isPopUpToInclusive
    332          */
    333         @NonNull
    334         public Builder setPopUpTo(@IdRes int destinationId, boolean inclusive) {
    335             mPopUpTo = destinationId;
    336             mPopUpToInclusive = inclusive;
    337             return this;
    338         }
    339 
    340         /**
    341          * Sets a custom Animation or Animator resource for the enter animation.
    342          *
    343          * <p>Note: Animator resources are not supported for navigating to a new Activity</p>
    344          * @param enterAnim Custom animation to run
    345          * @return this Builder
    346          * @see NavOptions#getEnterAnim()
    347          */
    348         @NonNull
    349         public Builder setEnterAnim(@AnimRes @AnimatorRes int enterAnim) {
    350             mEnterAnim = enterAnim;
    351             return this;
    352         }
    353 
    354         /**
    355          * Sets a custom Animation or Animator resource for the exit animation.
    356          *
    357          * <p>Note: Animator resources are not supported for navigating to a new Activity</p>
    358          * @param exitAnim Custom animation to run
    359          * @return this Builder
    360          * @see NavOptions#getExitAnim()
    361          */
    362         @NonNull
    363         public Builder setExitAnim(@AnimRes @AnimatorRes int exitAnim) {
    364             mExitAnim = exitAnim;
    365             return this;
    366         }
    367 
    368         /**
    369          * Sets a custom Animation or Animator resource for the enter animation
    370          * when popping off the back stack.
    371          *
    372          * <p>Note: Animator resources are not supported for navigating to a new Activity</p>
    373          * @param popEnterAnim Custom animation to run
    374          * @return this Builder
    375          * @see NavOptions#getPopEnterAnim()
    376          */
    377         @NonNull
    378         public Builder setPopEnterAnim(@AnimRes @AnimatorRes int popEnterAnim) {
    379             mPopEnterAnim = popEnterAnim;
    380             return this;
    381         }
    382 
    383         /**
    384          * Sets a custom Animation or Animator resource for the exit animation
    385          * when popping off the back stack.
    386          *
    387          * <p>Note: Animator resources are not supported for navigating to a new Activity</p>
    388          * @param popExitAnim Custom animation to run
    389          * @return this Builder
    390          * @see NavOptions#getPopExitAnim()
    391          */
    392         @NonNull
    393         public Builder setPopExitAnim(@AnimRes @AnimatorRes int popExitAnim) {
    394             mPopExitAnim = popExitAnim;
    395             return this;
    396         }
    397 
    398         /**
    399          * @return a constructed NavOptions
    400          */
    401         @NonNull
    402         public NavOptions build() {
    403             return new NavOptions(mLaunchMode, mPopUpTo, mPopUpToInclusive,
    404                     mEnterAnim, mExitAnim, mPopEnterAnim, mPopExitAnim);
    405         }
    406     }
    407 }
    408