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