1 /*
<lambda>null2 * Copyright 2018 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.ui
17
18 import android.view.Menu
19 import android.view.MenuItem
20 import androidx.customview.widget.Openable
21 import androidx.drawerlayout.widget.DrawerLayout
22 import androidx.navigation.NavDestination
23 import androidx.navigation.NavDestination.Companion.hierarchy
24 import androidx.navigation.NavGraph
25 import androidx.navigation.NavGraph.Companion.findStartDestination
26 import androidx.navigation.ui.AppBarConfiguration.OnNavigateUpListener
27 import java.util.HashSet
28
29 /**
30 * Configuration options for [NavigationUI] methods that interact with implementations of the app
31 * bar pattern such as [androidx.appcompat.widget.Toolbar],
32 * [com.google.android.material.appbar.CollapsingToolbarLayout], and
33 * [androidx.appcompat.app.ActionBar].
34 */
35 public class AppBarConfiguration
36 private constructor(
37 /**
38 * The set of destinations by id considered at the top level of your information hierarchy. The
39 * Up button will not be displayed when on these destinations.
40 *
41 * @return The set of top level destinations by id.
42 */
43 public val topLevelDestinations: Set<Int>,
44 /**
45 * The [Openable] layout indicating that the Navigation button should be displayed as a drawer
46 * symbol when it is not being shown as an Up button.
47 *
48 * @return The Openable layout that should be toggled from the Navigation button
49 */
50 public val openableLayout: Openable?,
51 /**
52 * The [OnNavigateUpListener] that should be invoked if
53 * [androidx.navigation.NavController.navigateUp] returns `false`.
54 *
55 * @return a [OnNavigateUpListener] for providing custom up navigation logic, if one was set.
56 */
57 public val fallbackOnNavigateUpListener: OnNavigateUpListener?
58 ) {
59 /**
60 * Interface for providing custom 'up' behavior beyond what is provided by
61 * [androidx.navigation.NavController.navigateUp].
62 *
63 * @see Builder.setFallbackOnNavigateUpListener
64 * @see NavigationUI.navigateUp
65 */
66 public fun interface OnNavigateUpListener {
67 /**
68 * Callback for handling the Up button.
69 *
70 * @return true if the listener successfully navigated 'up'
71 */
72 public fun onNavigateUp(): Boolean
73 }
74
75 /**
76 * The [DrawerLayout] indicating that the Navigation button should be displayed as a drawer
77 * symbol when it is not being shown as an Up button.
78 *
79 * @return The DrawerLayout that should be toggled from the Navigation button
80 */
81 @get:Deprecated("Use {@link #getOpenableLayout()}.")
82 public val drawerLayout: DrawerLayout?
83 get() =
84 if (openableLayout is DrawerLayout) {
85 openableLayout
86 } else null
87
88 /**
89 * Determines whether a [NavDestination] is a top level destination in [AppBarConfiguration].
90 *
91 * Returns true if the [NavDestination] was added directly as a top level destination via
92 * [AppBarConfiguration.Builder] constructors such as
93 * `AppBarConfiguration.Builder(topLevelDestinationIds: Set<Int>)`. If destination was added
94 * with a [AppBarConfiguration.Builder] that could take in a graph, i.e.
95 * `AppBarConfiguration.Builder(NavGraph)` or`AppBarConfiguration.Builder(Menu)`, this helper
96 * will return true if the destination is the start destination of a graph (including nested
97 * graphs i.e. [MenuItem] that are also [NavGraph]), or an individual [MenuItem] within the
98 * [Menu].
99 *
100 * @param destination the [NavDestination] to check whether it is a topLevelDestination
101 */
102 public fun isTopLevelDestination(destination: NavDestination): Boolean {
103 return destination.hierarchy.any { parent ->
104 when (parent.id in topLevelDestinations) {
105 true ->
106 if (parent is NavGraph) {
107 destination.id == parent.findStartDestination().id
108 } else true
109 else -> false
110 }
111 }
112 }
113
114 /** The Builder class for constructing new [AppBarConfiguration] instances. */
115 public class Builder {
116 private val topLevelDestinations: MutableSet<Int> = HashSet()
117 private var openableLayout: Openable? = null
118 private var fallbackOnNavigateUpListener: OnNavigateUpListener? = null
119
120 /**
121 * Create a new Builder whose only top level destination is the start destination of the
122 * given [NavGraph]. The Up button will not be displayed when on the start destination of
123 * the graph.
124 *
125 * @param navGraph The NavGraph whose start destination should be considered the only top
126 * level destination. The Up button will not be displayed when on the start destination of
127 * the graph.
128 */
129 public constructor(navGraph: NavGraph) {
130 topLevelDestinations.add(navGraph.findStartDestination().id)
131 }
132
133 /**
134 * Create a new Builder using a [Menu] containing all top level destinations. It is expected
135 * that the [menu item id][MenuItem.getItemId] of each item corresponds with a destination
136 * in your navigation graph. The Up button will not be displayed when on these destinations.
137 *
138 * @param topLevelMenu A Menu containing MenuItems corresponding with the destinations
139 * considered at the top level of your information hierarchy. The Up button will not be
140 * displayed when on these destinations.
141 */
142 public constructor(topLevelMenu: Menu) {
143 val size = topLevelMenu.size()
144 for (index in 0 until size) {
145 val item = topLevelMenu.getItem(index)
146 topLevelDestinations.add(item.itemId)
147 }
148 }
149
150 /**
151 * Create a new Builder with a specific set of top level destinations. The Up button will
152 * not be displayed when on these destinations.
153 *
154 * @param topLevelDestinationIds The set of destinations by id considered at the top level
155 * of your information hierarchy. The Up button will not be displayed when on these
156 * destinations.
157 */
158 public constructor(vararg topLevelDestinationIds: Int) {
159 for (destinationId in topLevelDestinationIds) {
160 topLevelDestinations.add(destinationId)
161 }
162 }
163
164 /**
165 * Create a new Builder with a specific set of top level destinations. The Up button will
166 * not be displayed when on these destinations.
167 *
168 * @param topLevelDestinationIds The set of destinations by id considered at the top level
169 * of your information hierarchy. The Up button will not be displayed when on these
170 * destinations.
171 */
172 public constructor(topLevelDestinationIds: Set<Int>) {
173 topLevelDestinations.addAll(topLevelDestinationIds)
174 }
175
176 /**
177 * Display the Navigation button as a drawer symbol when it is not being shown as an Up
178 * button.
179 *
180 * @param drawerLayout The DrawerLayout that should be toggled from the Navigation button
181 * @return this [Builder]
182 */
183 @Deprecated("Use {@link #setOpenableLayout(Openable)}.")
184 public fun setDrawerLayout(drawerLayout: DrawerLayout?): Builder {
185 openableLayout = drawerLayout
186 return this
187 }
188
189 /**
190 * Display the Navigation button as a drawer symbol when it is not being shown as an Up
191 * button.
192 *
193 * @param openableLayout The Openable layout that should be toggled from the Navigation
194 * button
195 * @return this [Builder]
196 */
197 public fun setOpenableLayout(openableLayout: Openable?): Builder {
198 this.openableLayout = openableLayout
199 return this
200 }
201
202 /**
203 * Adds a [OnNavigateUpListener] that will be called as a fallback if the default behavior
204 * of [androidx.navigation.NavController.navigateUp] returns `false`.
205 *
206 * @param fallbackOnNavigateUpListener Listener that will be invoked if
207 * [androidx.navigation.NavController.navigateUp] returns `false`.
208 * @return this [Builder]
209 */
210 public fun setFallbackOnNavigateUpListener(
211 fallbackOnNavigateUpListener: OnNavigateUpListener?
212 ): Builder {
213 this.fallbackOnNavigateUpListener = fallbackOnNavigateUpListener
214 return this
215 }
216
217 /**
218 * Construct the [AppBarConfiguration] instance.
219 *
220 * @return a valid [AppBarConfiguration]
221 */
222 /* new AppBarConfiguration() must be private to avoid
223 conflicting with the public AppBarConfiguration.kt */
224 public fun build(): AppBarConfiguration {
225 return AppBarConfiguration(
226 topLevelDestinations,
227 openableLayout,
228 fallbackOnNavigateUpListener
229 )
230 }
231 }
232 }
233
234 /**
235 * Configuration options for [NavigationUI] methods that interact with implementations of the app
236 * bar pattern such as [androidx.appcompat.widget.Toolbar],
237 * [com.google.android.material.appbar.CollapsingToolbarLayout], and
238 * [androidx.appcompat.app.ActionBar].
239 *
240 * @param navGraph The [NavGraph] whose start destination should be considered the only top level
241 * destination. The Up button will not be displayed when on the start destination of the graph.
242 * @param drawerLayout The Openable layout that should be toggled from the Navigation button. The
243 * the Navigation button will show a drawer symbol when it is not being shown as an Up button.
244 * @param fallbackOnNavigateUpListener Lambda that will be invoked if
245 * [androidx.navigation.NavController.navigateUp] returns `false`
246 */
247 @Suppress("FunctionName", "NOTHING_TO_INLINE") /* Acts like a constructor */
AppBarConfigurationnull248 public inline fun AppBarConfiguration(
249 navGraph: NavGraph,
250 drawerLayout: Openable? = null,
251 noinline fallbackOnNavigateUpListener: () -> Boolean = { false }
252 ): AppBarConfiguration =
253 AppBarConfiguration.Builder(navGraph)
254 .setOpenableLayout(drawerLayout)
255 .setFallbackOnNavigateUpListener(fallbackOnNavigateUpListener)
256 .build()
257
258 /**
259 * Configuration options for [NavigationUI] methods that interact with implementations of the app
260 * bar pattern such as [androidx.appcompat.widget.Toolbar],
261 * [com.google.android.material.appbar.CollapsingToolbarLayout], and
262 * [androidx.appcompat.app.ActionBar].
263 *
264 * @param topLevelMenu A Menu containing MenuItems corresponding with the destinations considered at
265 * the top level of your information hierarchy. The Up button will not be displayed when on these
266 * destinations.
267 * @param drawerLayout The Openable layout that should be toggled from the Navigation button. The
268 * the Navigation button will show a drawer symbol when it is not being shown as an Up button.
269 * @param fallbackOnNavigateUpListener Lambda that will be invoked if
270 * [androidx.navigation.NavController.navigateUp] returns `false`
271 */
272 @Suppress("FunctionName", "NOTHING_TO_INLINE") /* Acts like a constructor */
AppBarConfigurationnull273 public inline fun AppBarConfiguration(
274 topLevelMenu: Menu,
275 drawerLayout: Openable? = null,
276 noinline fallbackOnNavigateUpListener: () -> Boolean = { false }
277 ): AppBarConfiguration =
278 AppBarConfiguration.Builder(topLevelMenu)
279 .setOpenableLayout(drawerLayout)
280 .setFallbackOnNavigateUpListener(fallbackOnNavigateUpListener)
281 .build()
282
283 /**
284 * Configuration options for [NavigationUI] methods that interact with implementations of the app
285 * bar pattern such as [androidx.appcompat.widget.Toolbar],
286 * [com.google.android.material.appbar.CollapsingToolbarLayout], and
287 * [androidx.appcompat.app.ActionBar].
288 *
289 * @param topLevelDestinationIds The set of destinations by id considered at the top level of your
290 * information hierarchy. The Up button will not be displayed when on these destinations.
291 * @param drawerLayout The Openable layout that should be toggled from the Navigation button. The
292 * the Navigation button will show a drawer symbol when it is not being shown as an Up button.
293 * @param fallbackOnNavigateUpListener Lambda that will be invoked if
294 * [androidx.navigation.NavController.navigateUp] returns `false`
295 */
296 @Suppress("FunctionName", "NOTHING_TO_INLINE") /* Acts like a constructor */
AppBarConfigurationnull297 public inline fun AppBarConfiguration(
298 topLevelDestinationIds: Set<Int>,
299 drawerLayout: Openable? = null,
300 noinline fallbackOnNavigateUpListener: () -> Boolean = { false }
301 ): AppBarConfiguration =
302 AppBarConfiguration.Builder(topLevelDestinationIds)
303 .setOpenableLayout(drawerLayout)
304 .setFallbackOnNavigateUpListener(fallbackOnNavigateUpListener)
305 .build()
306