NavOptions.kt

/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package androidx.navigation

import androidx.annotation.AnimRes
import androidx.annotation.AnimatorRes
import androidx.annotation.IdRes
import androidx.navigation.NavDestination.Companion.createRoute

/**
 * NavOptions stores special options for navigate actions
 */
public class NavOptions internal constructor(
    private val singleTop: Boolean,
    private val restoreState: Boolean,
    /**
     * The destination to pop up to before navigating. When set, all non-matching destinations
     * should be popped from the back stack.
     * @return the destinationId to pop up to, clearing all intervening destinations
     * @see Builder.setPopUpTo
     *
     * @see isPopUpToInclusive
     * @see shouldPopUpToSaveState
     */
    @field:IdRes @get:IdRes @param:IdRes
    public val popUpToId: Int,
    private val popUpToInclusive: Boolean,
    private val popUpToSaveState: Boolean,
    /**
     * The custom enter Animation/Animator that should be run.
     * @return the resource id of a Animation or Animator or -1 if none.
     */
    @get:AnimatorRes @get:AnimRes @param:AnimRes @param:AnimatorRes
    public val enterAnim: Int,
    /**
     * The custom exit Animation/Animator that should be run.
     * @return the resource id of a Animation or Animator or -1 if none.
     */
    @get:AnimatorRes @get:AnimRes @param:AnimRes @param:AnimatorRes
    public val exitAnim: Int,
    /**
     * The custom enter Animation/Animator that should be run when this destination is
     * popped from the back stack.
     * @return the resource id of a Animation or Animator or -1 if none.
     */
    @get:AnimatorRes @get:AnimRes @param:AnimRes @param:AnimatorRes
    public val popEnterAnim: Int,
    /**
     * The custom exit Animation/Animator that should be run when this destination is
     * popped from the back stack.
     * @return the resource id of a Animation or Animator or -1 if none.
     */
    @get:AnimatorRes @get:AnimRes @param:AnimRes @param:AnimatorRes
    public val popExitAnim: Int
) {
    /**
     * The destination to pop up to before navigating. When set, all non-matching destinations
     * should be popped from the back stack.
     * @return the destinationId to pop up to, clearing all intervening destinations
     * @see Builder.setPopUpTo
     *
     * @see isPopUpToInclusive
     * @see shouldPopUpToSaveState
     */
    @IdRes
    @Deprecated("Use popUpToId instead.", ReplaceWith("popUpToId"))
    public fun getPopUpTo(): Int = popUpToId

    /**
     * Route for the destination to pop up to before navigating. When set, all non-matching
     * destinations should be popped from the back stack.
     * @return the destination route to pop up to, clearing all intervening destinations
     * @see Builder.setPopUpTo
     *
     * @see isPopUpToInclusive
     * @see shouldPopUpToSaveState
     */
    public var popUpToRoute: String? = null
        private set

    /**
     * NavOptions stores special options for navigate actions
     */
    internal constructor(
        singleTop: Boolean,
        restoreState: Boolean,
        popUpToRoute: String?,
        popUpToInclusive: Boolean,
        popUpToSaveState: Boolean,
        enterAnim: Int,
        exitAnim: Int,
        popEnterAnim: Int,
        popExitAnim: Int
    ) : this(
        singleTop,
        restoreState,
        createRoute(popUpToRoute).hashCode(),
        popUpToInclusive,
        popUpToSaveState,
        enterAnim,
        exitAnim,
        popEnterAnim,
        popExitAnim
    ) {
        this.popUpToRoute = popUpToRoute
    }

    /**
     * Whether this navigation action should launch as single-top (i.e., there will be at most
     * one copy of a given destination on the top of the back stack).
     *
     *
     * This functions similarly to how [android.content.Intent.FLAG_ACTIVITY_SINGLE_TOP]
     * works with activities.
     */
    public fun shouldLaunchSingleTop(): Boolean {
        return singleTop
    }

    /**
     * Whether this navigation action should restore any state previously saved
     * by [Builder.setPopUpTo] or the `popUpToSaveState` attribute.
     */
    public fun shouldRestoreState(): Boolean {
        return restoreState
    }

    /**
     * Whether the destination set in [getPopUpTo] should be popped from the back stack.
     * @see Builder.setPopUpTo
     *
     * @see NavOptions.getPopUpTo
     */
    public fun isPopUpToInclusive(): Boolean {
        return popUpToInclusive
    }

    /**
     * Whether the back stack and the state of all destinations between the
     * current destination and [popUpToId] should be saved for later restoration via
     * [Builder.setRestoreState] or the `restoreState` attribute using the same ID
     * as [popUpToId] (note: this matching ID is true whether [isPopUpToInclusive] is true or
     * false).
     */
    public fun shouldPopUpToSaveState(): Boolean {
        return popUpToSaveState
    }

    override fun equals(other: Any?): Boolean {
        if (this === other) return true
        if (other == null || javaClass != other.javaClass) return false
        val that = other as NavOptions
        return singleTop == that.singleTop &&
            restoreState == that.restoreState &&
            popUpToId == that.popUpToId &&
            popUpToRoute == that.popUpToRoute &&
            popUpToInclusive == that.popUpToInclusive &&
            popUpToSaveState == that.popUpToSaveState &&
            enterAnim == that.enterAnim &&
            exitAnim == that.exitAnim &&
            popEnterAnim == that.popEnterAnim &&
            popExitAnim == that.popExitAnim
    }

    override fun hashCode(): Int {
        var result = if (shouldLaunchSingleTop()) 1 else 0
        result = 31 * result + if (shouldRestoreState()) 1 else 0
        result = 31 * result + popUpToId
        result = 31 * result + popUpToRoute.hashCode()
        result = 31 * result + if (isPopUpToInclusive()) 1 else 0
        result = 31 * result + if (shouldPopUpToSaveState()) 1 else 0
        result = 31 * result + enterAnim
        result = 31 * result + exitAnim
        result = 31 * result + popEnterAnim
        result = 31 * result + popExitAnim
        return result
    }

    /**
     * Builder for constructing new instances of NavOptions.
     */
    public class Builder {
        private var singleTop = false
        private var restoreState = false

        @IdRes
        private var popUpToId = -1
        private var popUpToRoute: String? = null
        private var popUpToInclusive = false
        private var popUpToSaveState = false

        @AnimRes
        @AnimatorRes
        private var enterAnim = -1

        @AnimRes
        @AnimatorRes
        private var exitAnim = -1

        @AnimRes
        @AnimatorRes
        private var popEnterAnim = -1

        @AnimRes
        @AnimatorRes
        private var popExitAnim = -1

        /**
         * Launch a navigation target as single-top if you are making a lateral navigation
         * between instances of the same target (e.g. detail pages about similar data items)
         * that should not preserve history.
         *
         * @param singleTop true to launch as single-top
         */
        public fun setLaunchSingleTop(singleTop: Boolean): Builder {
            this.singleTop = singleTop
            return this
        }

        /**
         * Whether this navigation action should restore any state previously saved
         * by [setPopUpTo] or the `popUpToSaveState` attribute. If no state was
         * previously saved with the destination ID being navigated to, this has no effect.
         */
        @SuppressWarnings("MissingGetterMatchingBuilder")
        public fun setRestoreState(restoreState: Boolean): Builder {
            this.restoreState = restoreState
            return this
        }

        /**
         * Pop up to a given destination before navigating. This pops all non-matching destinations
         * from the back stack until this destination is found.
         *
         * @param destinationId The destination to pop up to, clearing all intervening destinations.
         * @param inclusive true to also pop the given destination from the back stack.
         * @param saveState true if the back stack and the state of all destinations between the
         * current destination and [destinationId] should be saved for later restoration via
         * [setRestoreState] or the `restoreState` attribute using the same ID
         * as [popUpToId] (note: this matching ID is true whether [inclusive] is true or
         * false).
         * @return this Builder
         *
         * @see NavOptions.popUpToId
         * @see NavOptions.isPopUpToInclusive
         */
        @JvmOverloads
        public fun setPopUpTo(
            @IdRes destinationId: Int,
            inclusive: Boolean,
            saveState: Boolean = false
        ): Builder {
            popUpToId = destinationId
            popUpToRoute = null
            popUpToInclusive = inclusive
            popUpToSaveState = saveState
            return this
        }

        /**
         * Pop up to a given destination before navigating. This pops all non-matching destinations
         * from the back stack until this destination is found.
         *
         * @param route route for destination to pop up to, clearing all intervening destinations.
         * @param inclusive true to also pop the given destination from the back stack.
         * @param saveState true if the back stack and the state of all destinations between the
         * current destination and [route] should be saved for later restoration via
         * [setRestoreState] or the `restoreState` attribute using the same ID
         * as [popUpToRoute] (note: this matching ID is true whether [inclusive] is true or
         * false).
         * @return this Builder
         *
         * @see NavOptions.popUpToId
         * @see NavOptions.isPopUpToInclusive
         */
        @JvmOverloads
        public fun setPopUpTo(
            route: String?,
            inclusive: Boolean,
            saveState: Boolean = false
        ): Builder {
            popUpToRoute = route
            popUpToId = -1
            popUpToInclusive = inclusive
            popUpToSaveState = saveState
            return this
        }

        /**
         * Sets a custom Animation or Animator resource for the enter animation.
         *
         * Note: Animator resources are not supported for navigating to a new Activity
         * @param enterAnim Custom animation to run
         * @return this Builder
         *
         * @see NavOptions.enterAnim
         */
        public fun setEnterAnim(@AnimRes @AnimatorRes enterAnim: Int): Builder {
            this.enterAnim = enterAnim
            return this
        }

        /**
         * Sets a custom Animation or Animator resource for the exit animation.
         *
         * Note: Animator resources are not supported for navigating to a new Activity
         * @param exitAnim Custom animation to run
         * @return this Builder
         *
         * @see NavOptions.exitAnim
         */
        public fun setExitAnim(@AnimRes @AnimatorRes exitAnim: Int): Builder {
            this.exitAnim = exitAnim
            return this
        }

        /**
         * Sets a custom Animation or Animator resource for the enter animation
         * when popping off the back stack.
         *
         * Note: Animator resources are not supported for navigating to a new Activity
         * @param popEnterAnim Custom animation to run
         * @return this Builder
         *
         * @see NavOptions.popEnterAnim
         */
        public fun setPopEnterAnim(@AnimRes @AnimatorRes popEnterAnim: Int): Builder {
            this.popEnterAnim = popEnterAnim
            return this
        }

        /**
         * Sets a custom Animation or Animator resource for the exit animation
         * when popping off the back stack.
         *
         * Note: Animator resources are not supported for navigating to a new Activity
         * @param popExitAnim Custom animation to run
         * @return this Builder
         *
         * @see NavOptions.popExitAnim
         */
        public fun setPopExitAnim(@AnimRes @AnimatorRes popExitAnim: Int): Builder {
            this.popExitAnim = popExitAnim
            return this
        }

        /**
         * @return a constructed NavOptions
         */
        public fun build(): NavOptions {
            return if (popUpToRoute != null)
                NavOptions(
                    singleTop, restoreState,
                    popUpToRoute, popUpToInclusive, popUpToSaveState,
                    enterAnim, exitAnim, popEnterAnim, popExitAnim
                )
            else
                NavOptions(
                    singleTop, restoreState,
                    popUpToId, popUpToInclusive, popUpToSaveState,
                    enterAnim, exitAnim, popEnterAnim, popExitAnim
                )
        }
    }
}