1 /* 2 * 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 package androidx.navigation 17 18 import androidx.annotation.AnimRes 19 import androidx.annotation.AnimatorRes 20 import androidx.annotation.IdRes 21 import androidx.navigation.NavDestination.Companion.createRoute 22 import androidx.navigation.serialization.generateHashCode 23 import kotlin.reflect.KClass 24 import kotlinx.serialization.InternalSerializationApi 25 import kotlinx.serialization.serializer 26 27 public actual class NavOptions 28 internal constructor( 29 private val singleTop: Boolean, 30 private val restoreState: Boolean, 31 /** 32 * The destination to pop up to before navigating. When set, all non-matching destinations 33 * should be popped from the back stack. 34 * 35 * @return the destinationId to pop up to, clearing all intervening destinations 36 * @see Builder.setPopUpTo 37 * @see isPopUpToInclusive 38 * @see shouldPopUpToSaveState 39 */ 40 @field:IdRes @get:IdRes @param:IdRes public actual val popUpToId: Int, 41 private val popUpToInclusive: Boolean, 42 private val popUpToSaveState: Boolean, 43 /** 44 * The custom enter Animation/Animator that should be run. 45 * 46 * @return the resource id of a Animation or Animator or -1 if none. 47 */ 48 @get:AnimatorRes @get:AnimRes @param:AnimRes @param:AnimatorRes public val enterAnim: Int, 49 /** 50 * The custom exit Animation/Animator that should be run. 51 * 52 * @return the resource id of a Animation or Animator or -1 if none. 53 */ 54 @get:AnimatorRes @get:AnimRes @param:AnimRes @param:AnimatorRes public val exitAnim: Int, 55 /** 56 * The custom enter Animation/Animator that should be run when this destination is popped from 57 * the back stack. 58 * 59 * @return the resource id of a Animation or Animator or -1 if none. 60 */ 61 @get:AnimatorRes @get:AnimRes @param:AnimRes @param:AnimatorRes public val popEnterAnim: Int, 62 /** 63 * The custom exit Animation/Animator that should be run when this destination is popped from 64 * the back stack. 65 * 66 * @return the resource id of a Animation or Animator or -1 if none. 67 */ 68 @get:AnimatorRes @get:AnimRes @param:AnimRes @param:AnimatorRes public val popExitAnim: Int 69 ) { 70 /** 71 * The destination to pop up to before navigating. When set, all non-matching destinations 72 * should be popped from the back stack. 73 * 74 * @return the destinationId to pop up to, clearing all intervening destinations 75 * @see Builder.setPopUpTo 76 * @see isPopUpToInclusive 77 * @see shouldPopUpToSaveState 78 */ 79 @IdRes 80 @Deprecated("Use popUpToId instead.", ReplaceWith("popUpToId")) getPopUpTonull81 public fun getPopUpTo(): Int = popUpToId 82 83 public actual var popUpToRoute: String? = null 84 private set 85 86 public actual var popUpToRouteClass: KClass<*>? = null 87 private set 88 89 public actual var popUpToRouteObject: Any? = null 90 private set 91 92 /** NavOptions stores special options for navigate actions */ 93 internal constructor( 94 singleTop: Boolean, 95 restoreState: Boolean, 96 popUpToRoute: String?, 97 popUpToInclusive: Boolean, 98 popUpToSaveState: Boolean, 99 enterAnim: Int, 100 exitAnim: Int, 101 popEnterAnim: Int, 102 popExitAnim: Int 103 ) : this( 104 singleTop, 105 restoreState, 106 createRoute(popUpToRoute).hashCode(), 107 popUpToInclusive, 108 popUpToSaveState, 109 enterAnim, 110 exitAnim, 111 popEnterAnim, 112 popExitAnim 113 ) { 114 this.popUpToRoute = popUpToRoute 115 } 116 117 /** NavOptions stores special options for navigate actions */ 118 @OptIn(InternalSerializationApi::class) 119 internal constructor( 120 singleTop: Boolean, 121 restoreState: Boolean, 122 popUpToRouteClass: KClass<*>?, 123 popUpToInclusive: Boolean, 124 popUpToSaveState: Boolean, 125 enterAnim: Int, 126 exitAnim: Int, 127 popEnterAnim: Int, 128 popExitAnim: Int 129 ) : this( 130 singleTop, 131 restoreState, 132 popUpToRouteClass!!.serializer().generateHashCode(), 133 popUpToInclusive, 134 popUpToSaveState, 135 enterAnim, 136 exitAnim, 137 popEnterAnim, 138 popExitAnim 139 ) { 140 this.popUpToRouteClass = popUpToRouteClass 141 } 142 143 /** NavOptions stores special options for navigate actions */ 144 @OptIn(InternalSerializationApi::class) 145 internal constructor( 146 singleTop: Boolean, 147 restoreState: Boolean, 148 popUpToRouteObject: Any, 149 popUpToInclusive: Boolean, 150 popUpToSaveState: Boolean, 151 enterAnim: Int, 152 exitAnim: Int, 153 popEnterAnim: Int, 154 popExitAnim: Int 155 ) : this( 156 singleTop, 157 restoreState, 158 popUpToRouteObject::class.serializer().generateHashCode(), 159 popUpToInclusive, 160 popUpToSaveState, 161 enterAnim, 162 exitAnim, 163 popEnterAnim, 164 popExitAnim 165 ) { 166 this.popUpToRouteObject = popUpToRouteObject 167 } 168 shouldLaunchSingleTopnull169 public actual fun shouldLaunchSingleTop(): Boolean { 170 return singleTop 171 } 172 shouldRestoreStatenull173 public actual fun shouldRestoreState(): Boolean { 174 return restoreState 175 } 176 isPopUpToInclusivenull177 public actual fun isPopUpToInclusive(): Boolean { 178 return popUpToInclusive 179 } 180 shouldPopUpToSaveStatenull181 public actual fun shouldPopUpToSaveState(): Boolean { 182 return popUpToSaveState 183 } 184 equalsnull185 override fun equals(other: Any?): Boolean { 186 if (this === other) return true 187 if (other == null || other !is NavOptions) return false 188 return singleTop == other.singleTop && 189 restoreState == other.restoreState && 190 popUpToId == other.popUpToId && 191 popUpToRoute == other.popUpToRoute && 192 popUpToRouteClass == other.popUpToRouteClass && 193 popUpToRouteObject == other.popUpToRouteObject && 194 popUpToInclusive == other.popUpToInclusive && 195 popUpToSaveState == other.popUpToSaveState && 196 enterAnim == other.enterAnim && 197 exitAnim == other.exitAnim && 198 popEnterAnim == other.popEnterAnim && 199 popExitAnim == other.popExitAnim 200 } 201 hashCodenull202 override fun hashCode(): Int { 203 var result = if (shouldLaunchSingleTop()) 1 else 0 204 result = 31 * result + if (shouldRestoreState()) 1 else 0 205 result = 31 * result + popUpToId 206 result = 31 * result + popUpToRoute.hashCode() 207 result = 31 * result + popUpToRouteClass.hashCode() 208 result = 31 * result + popUpToRouteObject.hashCode() 209 result = 31 * result + if (isPopUpToInclusive()) 1 else 0 210 result = 31 * result + if (shouldPopUpToSaveState()) 1 else 0 211 result = 31 * result + enterAnim 212 result = 31 * result + exitAnim 213 result = 31 * result + popEnterAnim 214 result = 31 * result + popExitAnim 215 return result 216 } 217 toStringnull218 override fun toString(): String { 219 val sb = StringBuilder() 220 sb.append(javaClass.simpleName) 221 sb.append("(") 222 if (singleTop) { 223 sb.append("launchSingleTop ") 224 } 225 if (restoreState) { 226 sb.append("restoreState ") 227 } 228 if (popUpToRoute != null || popUpToId != -1) 229 if (popUpToRoute != null) { 230 sb.append("popUpTo(") 231 if (popUpToRoute != null) { 232 sb.append(popUpToRoute) 233 } else if (popUpToRouteClass != null) { 234 sb.append(popUpToRouteClass) 235 } else if (popUpToRouteObject != null) { 236 sb.append(popUpToRouteObject) 237 } else { 238 sb.append("0x") 239 sb.append(Integer.toHexString(popUpToId)) 240 } 241 if (popUpToInclusive) { 242 sb.append(" inclusive") 243 } 244 if (popUpToSaveState) { 245 sb.append(" saveState") 246 } 247 sb.append(")") 248 } 249 if (enterAnim != -1 || exitAnim != -1 || popEnterAnim != -1 || popExitAnim != -1) { 250 sb.append("anim(enterAnim=0x") 251 sb.append(Integer.toHexString(enterAnim)) 252 sb.append(" exitAnim=0x") 253 sb.append(Integer.toHexString(exitAnim)) 254 sb.append(" popEnterAnim=0x") 255 sb.append(Integer.toHexString(popEnterAnim)) 256 sb.append(" popExitAnim=0x") 257 sb.append(Integer.toHexString(popExitAnim)) 258 sb.append(")") 259 } 260 return sb.toString() 261 } 262 263 public actual class Builder { 264 private var singleTop = false 265 private var restoreState = false 266 267 @IdRes private var popUpToId = -1 268 private var popUpToRoute: String? = null 269 private var popUpToRouteClass: KClass<*>? = null 270 private var popUpToRouteObject: Any? = null 271 private var popUpToInclusive = false 272 private var popUpToSaveState = false 273 274 @AnimRes @AnimatorRes private var enterAnim = -1 275 276 @AnimRes @AnimatorRes private var exitAnim = -1 277 278 @AnimRes @AnimatorRes private var popEnterAnim = -1 279 280 @AnimRes @AnimatorRes private var popExitAnim = -1 281 setLaunchSingleTopnull282 public actual fun setLaunchSingleTop(singleTop: Boolean): Builder { 283 this.singleTop = singleTop 284 return this 285 } 286 287 @SuppressWarnings("MissingGetterMatchingBuilder") setRestoreStatenull288 public actual fun setRestoreState(restoreState: Boolean): Builder { 289 this.restoreState = restoreState 290 return this 291 } 292 293 /** 294 * Pop up to a given destination before navigating. This pops all non-matching destinations 295 * from the back stack until this destination is found. 296 * 297 * @param destinationId The destination to pop up to, clearing all intervening destinations. 298 * @param inclusive true to also pop the given destination from the back stack. 299 * @param saveState true if the back stack and the state of all destinations between the 300 * current destination and [destinationId] should be saved for later restoration via 301 * [setRestoreState] or the `restoreState` attribute using the same ID as [popUpToId] 302 * (note: this matching ID is true if [inclusive] is true. If [inclusive] is false, this 303 * matching ID is the id of the last destination that is popped). 304 * @return this Builder 305 * @see NavOptions.popUpToId 306 * @see NavOptions.isPopUpToInclusive 307 */ 308 @JvmOverloads setPopUpTonull309 public fun setPopUpTo( 310 @IdRes destinationId: Int, 311 inclusive: Boolean, 312 saveState: Boolean = false 313 ): Builder { 314 popUpToId = destinationId 315 popUpToRoute = null 316 popUpToInclusive = inclusive 317 popUpToSaveState = saveState 318 return this 319 } 320 321 @JvmOverloads setPopUpTonull322 public actual fun setPopUpTo( 323 route: String?, 324 inclusive: Boolean, 325 saveState: Boolean 326 ): Builder { 327 popUpToRoute = route 328 popUpToId = -1 329 popUpToInclusive = inclusive 330 popUpToSaveState = saveState 331 return this 332 } 333 334 @JvmOverloads 335 @Suppress("MissingGetterMatchingBuilder") // no need for getter setPopUpTonull336 public actual inline fun <reified T : Any> setPopUpTo( 337 inclusive: Boolean, 338 saveState: Boolean 339 ): Builder { 340 setPopUpTo(T::class, inclusive, saveState) 341 return this 342 } 343 344 @JvmOverloads setPopUpTonull345 public actual fun <T : Any> setPopUpTo( 346 route: KClass<T>, 347 inclusive: Boolean, 348 saveState: Boolean 349 ): Builder { 350 popUpToRouteClass = route 351 popUpToId = -1 352 popUpToInclusive = inclusive 353 popUpToSaveState = saveState 354 return this 355 } 356 357 @JvmOverloads 358 @Suppress("MissingGetterMatchingBuilder") 359 @OptIn(InternalSerializationApi::class) setPopUpTonull360 public actual fun <T : Any> setPopUpTo( 361 route: T, 362 inclusive: Boolean, 363 saveState: Boolean 364 ): Builder { 365 popUpToRouteObject = route 366 setPopUpTo(route::class.serializer().generateHashCode(), inclusive, saveState) 367 return this 368 } 369 370 /** 371 * Sets a custom Animation or Animator resource for the enter animation. 372 * 373 * Note: Animator resources are not supported for navigating to a new Activity 374 * 375 * @param enterAnim Custom animation to run 376 * @return this Builder 377 * @see NavOptions.enterAnim 378 */ setEnterAnimnull379 public fun setEnterAnim(@AnimRes @AnimatorRes enterAnim: Int): Builder { 380 this.enterAnim = enterAnim 381 return this 382 } 383 384 /** 385 * Sets a custom Animation or Animator resource for the exit animation. 386 * 387 * Note: Animator resources are not supported for navigating to a new Activity 388 * 389 * @param exitAnim Custom animation to run 390 * @return this Builder 391 * @see NavOptions.exitAnim 392 */ setExitAnimnull393 public fun setExitAnim(@AnimRes @AnimatorRes exitAnim: Int): Builder { 394 this.exitAnim = exitAnim 395 return this 396 } 397 398 /** 399 * Sets a custom Animation or Animator resource for the enter animation when popping off the 400 * back stack. 401 * 402 * Note: Animator resources are not supported for navigating to a new Activity 403 * 404 * @param popEnterAnim Custom animation to run 405 * @return this Builder 406 * @see NavOptions.popEnterAnim 407 */ setPopEnterAnimnull408 public fun setPopEnterAnim(@AnimRes @AnimatorRes popEnterAnim: Int): Builder { 409 this.popEnterAnim = popEnterAnim 410 return this 411 } 412 413 /** 414 * Sets a custom Animation or Animator resource for the exit animation when popping off the 415 * back stack. 416 * 417 * Note: Animator resources are not supported for navigating to a new Activity 418 * 419 * @param popExitAnim Custom animation to run 420 * @return this Builder 421 * @see NavOptions.popExitAnim 422 */ setPopExitAnimnull423 public fun setPopExitAnim(@AnimRes @AnimatorRes popExitAnim: Int): Builder { 424 this.popExitAnim = popExitAnim 425 return this 426 } 427 buildnull428 public actual fun build(): NavOptions { 429 return if (popUpToRoute != null) { 430 NavOptions( 431 singleTop, 432 restoreState, 433 popUpToRoute, 434 popUpToInclusive, 435 popUpToSaveState, 436 enterAnim, 437 exitAnim, 438 popEnterAnim, 439 popExitAnim 440 ) 441 } else if (popUpToRouteClass != null) { 442 NavOptions( 443 singleTop, 444 restoreState, 445 popUpToRouteClass, 446 popUpToInclusive, 447 popUpToSaveState, 448 enterAnim, 449 exitAnim, 450 popEnterAnim, 451 popExitAnim 452 ) 453 } else if (popUpToRouteObject != null) { 454 NavOptions( 455 singleTop, 456 restoreState, 457 popUpToRouteObject!!, 458 popUpToInclusive, 459 popUpToSaveState, 460 enterAnim, 461 exitAnim, 462 popEnterAnim, 463 popExitAnim 464 ) 465 } else { 466 NavOptions( 467 singleTop, 468 restoreState, 469 popUpToId, 470 popUpToInclusive, 471 popUpToSaveState, 472 enterAnim, 473 exitAnim, 474 popEnterAnim, 475 popExitAnim 476 ) 477 } 478 } 479 } 480 } 481