Navigation.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 android.app.Activity
import android.os.Bundle
import android.view.View
import androidx.annotation.IdRes
import androidx.core.app.ActivityCompat
import java.lang.ref.WeakReference
/**
* Entry point for navigation operations.
*
* This class provides utilities for finding a relevant [NavController] instance from
* various common places in your application, or for performing navigation in response to
* UI events.
*/
public object Navigation {
/**
* Find a [NavController] given the id of a View and its containing
* [Activity]. This is a convenience wrapper around [findNavController].
*
* This method will locate the [NavController] associated with this view.
* This is automatically populated for the id of a [NavHost] and its children.
*
* @param activity The Activity hosting the view
* @param viewId The id of the view to search from
* @return the [NavController] associated with the view referenced by id
* @throws IllegalStateException if the given viewId does not correspond with a
* [NavHost] or is not within a NavHost.
*/
@JvmStatic
public fun findNavController(activity: Activity, @IdRes viewId: Int): NavController {
val view = ActivityCompat.requireViewById<View>(activity, viewId)
return findViewNavController(view)
?: throw IllegalStateException(
"Activity $activity does not have a NavController set on $viewId"
)
}
/**
* Find a [NavController] given a local [View].
*
* This method will locate the [NavController] associated with this view.
* This is automatically populated for views that are managed by a [NavHost]
* and is intended for use by various [listener][android.view.View.OnClickListener]
* interfaces.
*
* @param view the view to search from
* @return the locally scoped [NavController] to the given view
* @throws IllegalStateException if the given view does not correspond with a
* [NavHost] or is not within a NavHost.
*/
@JvmStatic
public fun findNavController(view: View): NavController {
return findViewNavController(view)
?: throw IllegalStateException("View $view does not have a NavController set")
}
/**
* Create an [android.view.View.OnClickListener] for navigating
* to a destination. This supports both navigating via an
* [action][NavDestination.getAction] and directly navigating to a destination.
*
* @param resId an [action][NavDestination.getAction] id or a destination id to
* navigate to when the view is clicked
* @param args arguments to pass to the final destination
* @return a new click listener for setting on an arbitrary view
*/
@JvmStatic
@JvmOverloads
public fun createNavigateOnClickListener(
@IdRes resId: Int,
args: Bundle? = null
): View.OnClickListener {
return View.OnClickListener { view -> findNavController(view).navigate(resId, args) }
}
/**
* Create an [android.view.View.OnClickListener] for navigating
* to a destination via a generated [NavDirections].
*
* @param directions directions that describe this navigation operation
* @return a new click listener for setting on an arbitrary view
*/
@JvmStatic
public fun createNavigateOnClickListener(directions: NavDirections): View.OnClickListener {
return View.OnClickListener { view -> findNavController(view).navigate(directions) }
}
/**
* Associates a NavController with the given View, allowing developers to use
* [findNavController] and [findNavController] with that
* View or any of its children to retrieve the NavController.
*
* This is generally called for you by the hosting [NavHost].
* @param view View that should be associated with the given NavController
* @param controller The controller you wish to later retrieve via
* [findNavController]
*/
@JvmStatic
public fun setViewNavController(view: View, controller: NavController?) {
view.setTag(R.id.nav_controller_view_tag, controller)
}
/**
* Recurse up the view hierarchy, looking for the NavController
* @param view the view to search from
* @return the locally scoped [NavController] to the given view, if found
*/
private fun findViewNavController(view: View): NavController? =
generateSequence(view) {
it.parent as? View?
}.mapNotNull {
getViewNavController(it)
}.firstOrNull()
@Suppress("UNCHECKED_CAST")
private fun getViewNavController(view: View): NavController? {
val tag = view.getTag(R.id.nav_controller_view_tag)
var controller: NavController? = null
if (tag is WeakReference<*>) {
controller = (tag as WeakReference<NavController>).get()
} else if (tag is NavController) {
controller = tag
}
return controller
}
}