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