[Daniel Galpin] Adventures in Navigation

[Daniel Galpin] Adventures in Navigation

Presentation from GDG DevFest Ukraine 2018 - the biggest community-driven Google tech conference in the CEE.

Learn more at: https://devfest.gdg.org.ua

__

Take a spin with the new Android Jetpack Navigation Components library announced at Google I/O. The Navigation Components let you keep all of your navigation information in one place and include support for Material components such as bottom navigation, the app drawer, and the overflow menu.

Dan will explore the basics and then dive into a series of more complex use cases. Learn about advanced topics such as deep-linking, leveraging Navigation to simplify modularization for instant apps and Dynamic App Delivery, using sub-graphs, manipulating the back-stack, sharing a ViewModel between navigation destinations and more. See what Navigation components can do for you!

3a6de6bc902de7f75c0e753b3202ed52?s=128

Google Developers Group Lviv

October 13, 2018
Tweet

Transcript

  1. Adventures in Navigation Dan Galpin @dagalpin

  2. Proprietary + Confidential Proprietary + Confidential Proprietary + Confidential Why

    Navigation
  3. Things weren't easy Fragment Transactions Deep Links Passing Arguments Error

    Prone Boilerplate Testing
  4. Things weren't consistent Deep Links Up and Back Navigation Drawer

    Bottom Navigation Menus
  5. Principles of Navigation

  6. Fixed Starting Destination

  7. Fixed Starting Destination

  8. Fixed Starting Destination*

  9. VS

  10. Up Navigation Traverses up the Hierarchy

  11. Up Navigation Traverses up the Hierarchy Parent Child

  12. Up Navigation Traverses up the Hierarchy Parent Child

  13. Up Navigation Traverses up the Hierarchy

  14. ???

  15. ???

  16. Up Navigation Traverses up the Hierarchy

  17. == Within Your Task

  18. != When Your Activity is Part of the Task of

    Another App
  19. None
  20. Deep Linking

  21. Deep Linking

  22. Deep Linking

  23. Be Consistent

  24. Proprietary + Confidential Proprietary + Confidential Proprietary + Confidential A

    (quick) tour of the Navigation Component
  25. The Navigation Component // Main Navigation Component code implementation "android.arch.navigation:navigation-fragment:$version"

    // NavigationUI static methods to setup action/toolbars, drawers and menus implementation "android.arch.navigation:navigation-ui:$version" // Safe Args code generation plugin for type safety apply plugin: 'androidx.navigation.safeargs' // Test helpers androidTestImplementation "android.arch.navigation:navigation-testing:$nav_version"
  26. The Navigation Component // Main Navigation Component code implementation "android.arch.navigation:navigation-fragment-ktx:$version"

    // NavigationUI static methods to setup action/toolbars, drawers and menus implementation "android.arch.navigation:navigation-ui-ktx:$version" // Safe Args code generation plugin for type safety (no KTX version) apply plugin: 'androidx.navigation.safeargs' // Test helpers androidTestImplementation "android.arch.navigation:navigation-testing-ktx:$nav_version"
  27. Navigation Graph (New Resource) NavHostFragment (Layout) NavController (Java/Kotlin) NavHost

  28. start destination destination destination destination destination destination destination destination

  29. action action action action action action action action action action

  30. None
  31. Navigation Graph (New Resource) NavHostFragment (Layout) NavController (Java/Kotlin) NavHost

  32. Navigation Graph (New Resource) NavHostFragment (Layout) NavController (Java/Kotlin) NavHost navController.navigate(<Destination

    or Action id>)
  33. NavHost NavController (Java/Kotlin) navController.navigate(R.id.in_game_dest) R.id.in_game_dest Navigation Graph (New Resource) NavHostFragment

    (Layout)
  34. NavHost NavController (Java/Kotlin) navController.navigate(R.id.win_action) R.id.win_action Navigation Graph (New Resource) NavHostFragment

    (Layout)
  35. • Visualizes navigation paths The Navigation Component

  36. The Navigation Component • Visualizes navigation paths • Defines navigation

    in one place
  37. The Navigation Component • Visualizes navigation paths • Defines navigation

    in one place • Reduces nav pattern boilerplate That's a lot of steps for a NavDrawer!
  38. The Navigation Component • Visualizes navigation paths • Defines navigation

    in one place • Reduces nav pattern boilerplate • Implements Material guidelines (including backstack)
  39. Getting Started

  40. Proprietary + Confidential Proprietary + Confidential Proprietary + Confidential General

    Guidance
  41. Fragment or Activity Destinations?

  42. It's possible to navigate to an Activity Terminal destination

  43. Activities for Global Navigation NavHost Global navigation

  44. Fragments for Content NavHost Content

  45. Benefits of Single Activity • Single navigation graph • Transition

    animations for property and views • Activity ViewModels can be shared amongst fragments NavHost ViewModel LiveData LiveData LiveData
  46. Destinations or Actions?

  47. • You can see them Use actions

  48. Use actions • You can see them • Your navigation

    graph will contain more information Transitions between destinations Argument passing Backstack manipulation
  49. Use actions • You can see them • Your navigation

    graph will contain more information • You can use safe args Argument passing
  50. Proprietary + Confidential Proprietary + Confidential Proprietary + Confidential Safe

    Args
  51. Safe Args Code Generation Plugin Uses your Navigation Graph to

    ensure type safety
  52. ID's Are Used to Represent Lots of Things • R.id.win_action

    // id for Action • R.id.win_destination // id for Destination • R.id.win_menu_item // id for Menu Item • R.id.win_image_view // id for ImageView • navController.navigate(R.id.win) // navigating to ¯\_(ツ)_/¯
  53. Using sensible naming conventions helps • R.id.win_action // id for

    Action • R.id.win_destination // id for Destination • R.id.win_menu_item // id for Menu Item • R.id.win_image_view // id for ImageView • navController.navigate(R.id.win.action) // id for action
  54. navController.navigate(R.id.image_view)

  55. navController.navigate(R.id.action_register_to_match) R.id.action_register_to_match

  56. navController.navigate(RegisterDirections.actionRegisterToMatch()) R.id.action_register_to_match

  57. val matchAction = RegisterDirections.actionRegisterToMatch() matchAction.setOpponent("User3466") navController.navigate(matchAction) Example with argument R.id.action_register_to_match

  58. val matchAction = RegisterDirections. actionRegisterToMatch("User3466") navController.navigate(matchAction) Example with argument without

    default R.id.action_register_to_match
  59. val args = MatchFragmentArgs.fromBundle(arguments) val opponent = args.opponent Match Fragment

    Fetching the arguments from the bundle
  60. Safe Args //build.gradle (project) classpath "android.arch.navigation:navigation-safe-args -gradle-plugin:$version" //build.gradle (app) apply

    plugin: 'androidx.navigation.safeargs' "Directions" for any destination with Actions, named after the class "Args" for every destination with argument(s)
  61. Proprietary + Confidential Proprietary + Confidential Proprietary + Confidential Navigation

    UI
  62. Up Button Overflow Menu Nav Drawer Bottom Nav

  63. Navigation Drawer Apps with five or more top-level destinations Apps

    with two or more levels of navigation hierarchy Quick navigation between unrelated destinations
  64. Start Destination Top Level Destinations

  65. Back Stack

  66. Message Detail Back Stack

  67. Back Stack

  68. Bottom Navigation Apps with three to five top-level destinations that

    need to be accessible anywhere in the app Mobile and Tablet only
  69. Start Destination

  70. Back Stack

  71. Back Stack

  72. Back Stack

  73. None
  74. None
  75. Activity Outer Navigation Host Fragment Inner Navigation Host Fragment

  76. Tabs Not supported by navigation since tabs don't change between

    destinations.
  77. <menu xmlns:android="http://schemas.android.com/apk/res/android"> <item android:id="@+id/rulesFragment" android:icon="@drawable/rules" android:title="@string/rules" /> <item android:id="@+id/aboutFragment" android:icon="@drawable/android"

    android:title="@string/about" /> </menu>
  78. Overflow Menu override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { super.onCreateOptionsMenu(menu,

    inflater) inflater.inflate(R.menu.overflow_menu, menu) MenuCompat.setGroupDividerEnabled(menu, true); } override fun onOptionsItemSelected(item: MenuItem): Boolean { return NavigationUI.onNavDestinationSelected(item, findNavController()) || super.onOptionsItemSelected(item) }
  79. Setup Bottom Navigation // In onCreate of Activity // Setup

    the NavDrawer by passing the NavigationView to NavigationUI NavigationUI.setupWithNavController(binding.bottomNav, navController)
  80. The Up Button - Fragment + Toolbar override fun onCreateView(inflater:

    LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { ... val navController = findNavController() NavigationUI.setupWithNavController(binding.toolbar, navController) ... }
  81. The Up Button - Activity + ActionBar override fun onCreate(savedInstanceState:

    Bundle?) { ... val navController = this.findNavController(R.id.myNavHostFragment) NavigationUI.setupActionBarWithNavController(this,navController) ... } override fun onSupportNavigateUp(): Boolean { val navController = this.findNavController(R.id.myNavHostFragment) return NavigationUI.navigateUp(navController) }
  82. override fun onCreate(savedInstanceState: Bundle?) { ... NavigationUI.setupActionBarWithNavController(this, navController, drawerLayout) NavigationUI.setupWithNavController(navView,

    navController) ... } override fun onSupportNavigateUp(): Boolean { return NavigationUI.navigateUp(drawerLayout, navController) } Setup a NavDrawer
  83. public static void setupWithNavController(@NonNull final NavigationView navigationView, @NonNull final NavController

    navController) { navigationView.setNavigationItemSelectedListener(new OnNavigationItemSelectedListener() { public boolean onNavigationItemSelected(@NonNull MenuItem item) { boolean handled = NavigationUI.onNavDestinationSelected(item, navController, true); if (handled) { ViewParent parent = navigationView.getParent(); if (parent instanceof DrawerLayout) { ((DrawerLayout)parent).closeDrawer(navigationView); } } return handled; } });
  84. static boolean onNavDestinationSelected(@NonNull MenuItem item, @NonNull NavController navController, boolean popUp)

    { ... if (popUp) { builder.setPopUpTo(findStartDestination(navController.getGraph()).getId(), false); } NavOptions options = builder.build(); ... popUp
  85. None
  86. Activity vs. Fragment for Detail • Is the toolbar global

    nav? • Is the bottom nav global nav? • Can we get the up button to function easily?
  87. NavHost NavHost

  88. Activity vs. Fragment for Detail • Is the toolbar global

    nav? ◦ No - it changes radically • Is the bottom nav global nav? ◦ Yes • Can we get the up button to function easily?
  89. Setup Action Bar, Tool Bar // Setting up other fragments

    without CollapsingToolbar NavigationUI.setupWithNavController(toolbar, navController) // Setting up DetailFragment with CollapsingToolbar Navigation NavigationUI.setupWithNavController(collapsingToolbarLayout, binding.toolbar, navController) // No need to do anything else for up button
  90. Activity vs. Fragment for Detail • Is the toolbar global

    nav? ◦ No - it changes radically • Is the bottom nav global nav? ◦ Yes • Can we get the up button to function easily? ◦ Yes • What about transitions? ◦ Looks a little strange
  91. Proprietary + Confidential Proprietary + Confidential Proprietary + Confidential Shared

    Element Transitions
  92. Shared Elements

  93. fun selectPlayerProfile( playerName: String, view: ImageView) { val navController =

    view.findNavController() val extras = FragmentNavigatorExtras( view to playerName ) navController.navigate( LeaderboardDirections.actionLeaderboardToUserProfile(playerName), extras) } Supporting Shared Elements in Navigation
  94. Shared Element Transitions // in user profile fragment val enterTransitionSet

    = TransitionSet() enterTransitionSet.addTransition(TransitionInflater.from(context) .inflateTransition(android.R.transition.move)) sharedElementEnterTransition = enterTransitionSet // in leaderboard fragment postponeEnterTransition() recyclerView.getViewTreeObserver() .addOnPreDrawListener { startPostponedEnterTransition() true } val returnTransitionSet = TransitionSet() returnTransitionSet.addTransition(TransitionInflater.from(context) .inflateTransition(android.R.transition.move)) sharedElementReturnTransition = returnTransitionSet
  95. Shared Element Transitions - Waiting for Images @BindingAdapter("sharedElementImageUrl") fun bindSharedImage(imageView:

    ImageView, url: String?) { Glide.with(fragment).load(url) .listener(object : RequestListener<Drawable?> { override fun onResourceReady(_: Drawable?, _: Any?, _: Target<Drawable?>?, _: DataSource?, _: Boolean): Boolean { fragment.startPostponedEnterTransition() return false } override fun onLoadFailed(e: GlideException?, _: Any?, _: Target<Drawable?>?, _: Boolean): Boolean { fragment.startPostponedEnterTransition() return false } }) .into(imageView) }
  96. Conditional Navigation

  97. Straight-forward val actionRegister = TitleScreenDirections.actionRegister() navController.navigate(actionRegister)

  98. Less so... • Decide whether to go to win or

    lose screen • Data not usually in the fragment • Avoid having lots of navigation logic in activities/fragments
  99. Guide to App Architecture • One way to do it,

    using Lifecycle libraries • developer.android.com/jetpack/docs/g uide
  100. Fragment Data Layer ViewModel LiveData LiveData LiveData Does actual navigation

    Provides any non-transient data Holds data needed to make navigation decisions Communicated decisions back to fragment via observation Where all the conditional navigation decisions can be made
  101. Navigates User answers last question Fragment Data Layer ViewModel LiveData

    LiveData LiveData Takes the data such as score, difficulty, etc and decides whether it's a win or lose Communicates win/loss to Fragment
  102. LiveData to Navigate Problem • LiveData holds data state ◦

    Ex. "This button is green" • Data state != to an event ◦ Ex. Anything that fires and is done ◦ "Show this notification" • Navigation is an event ◦ You never stay in the "navigating to Win" state Fragment Data Layer ViewModel LiveData LiveData LiveData Communicates win/loss to Fragment
  103. None
  104. Event (Wrapper around LiveData to only handle once) open class

    Event<out T>(private val content: T) { var hasBeenHandled = false private set // Allow external read but not write fun getContentIfNotHandled(): T? { return if (hasBeenHandled) { null } else { hasBeenHandled = true content } } fun peekContent(): T = content }
  105. Fragment - User answers last question // Call a ViewModel

    method from the Fragment when user finishes the quiz binding.quizFinishedButton.setOnClickListener { viewModel.quizFinished() }
  106. ViewModel - Decide win or lose // ViewModel and LiveData

    val navigateToWin = MutableLiveData<Event<Int>>() val navigateToGameOver = MutableLiveData<Event<Int>>() fun quizFinished() { //Do logic with score, rules, etc to see if game was won }
  107. ViewModel with LiveData Event - Communicate Navigation // ViewModel and

    LiveData val navigateToWin = MutableLiveData<Event<Int>>() val navigateToGameOver = MutableLiveData<Event<Int>>() fun quizFinished() { //Do logic with score, rules, etc to see if game was won if (wonGame) { navigateToWin.value = Event(score) } else { navigateToGameOver.value = Event(score) } }
  108. Fragment - Navigate! // Observer when navigation events happen viewModel.navigateToWin.observe(this,

    Observer { it.getContentIfNotHandled()?.let { score -> val action = InGameDirections.winAction() action.setScore(score) navController.navigate(action) } }) // Similar observer for gameOverAction
  109. Only visible to logged in users Login Flow Example

  110. None
  111. Login When I navigate or when my logged in state

    changes, check if I should be sent to the login flow.
  112. Login • Use nested graph for Login flow

  113. Nested Graphs • Good for sub-flows like login

  114. Nested Graphs • Good for sub-flows like login • Navigating

    to Nested Graphs will always send you to start destination Start Destination
  115. Login • Use nested graph for Login flow • Use

    global action
  116. Global Action • Has a destination, but no starting point

    Normal action Global action
  117. Global Action • Has a destination, but no starting point

    • Can be used by any destination in the graph
  118. Global Action • Has a destination, but no starting point

    • Can be used by any destination in the graph • An action not nested in a destination is a global action
  119. <navigation android:id="@+id/main_navigation" app:startDestination="@id/fragmentA"> <fragment android:id="@+id/fragmentA".../> <fragment android:id="@+id/fragmentB".../> <fragment android:id="@+id/fragmentC"...> <action

    ... /> </fragment> <fragment android:id="@+id/detailFragment"...> <action ... /> </fragment> <fragment android:id="@+id/fragmentDetailTwo"/> </navigation>
  120. <navigation android:id="@+id/main_navigation" app:startDestination="@id/fragmentA"> <fragment android:id="@+id/fragmentA".../> <fragment android:id="@+id/fragmentB".../> <fragment android:id="@+id/fragmentC"...> <action

    ... /> </fragment> <fragment android:id="@+id/detailFragment"...> <action ... /> </fragment> <fragment android:id="@+id/fragmentDetailTwo"/> <navigation android:id="@+id/login" app:startDestination="@id/loginFragment"> <!-- nested graph fragments here --> </navigation> </navigation> Login nested graph
  121. <navigation android:id="@+id/main_navigation" app:startDestination="@id/fragmentA"> <fragment android:id="@+id/fragmentA".../> <fragment android:id="@+id/fragmentB".../> <fragment android:id="@+id/fragmentC"...> <action

    ... /> </fragment> <fragment android:id="@+id/detailFragment"...> <action ... /> </fragment> <fragment android:id="@+id/fragmentDetailTwo"/> <navigation android:id="@+id/login" app:startDestination="@id/loginFragment"> <!-- nested graph fragments here --> </navigation> <action android:id="@+id/action_global_login" app:destination="@id/login" /> </navigation> Global action
  122. Login • Use nested graph for Login flow • Use

    global action • Use shared Activity ViewModel
  123. ViewModel ViewModel ViewModel ViewModel ViewModel Activity ViewModel for shared UI

    data
  124. LiveData Activity ViewModel currentDestinationId: LiveData<Int> isLoggedIn: LiveData<boolean> navigateToLogin: LiveData<Event<Unit>> Just

    mentioned how to set this up Depends on what you're doing to implement login You can use an onNavigatedListener
  125. OnNavigatedListener • Called as you arrive at a destination ◦

    Includes current destination • Useful for updating Navigation UI • Remember to remove listener
  126. OnNavigatedListener // In Activity onCreate navigationListener = NavController.OnNavigatedListener { controller,

    destination -> // Redirect navigation here } navController.addOnNavigatedListener(navigationListener) // In Activity onDestroy navController.removeOnNavigatedListener(navigationListener) Redirect navigation
  127. Login • Use nested graph for Login flow • Use

    global action • Tracking current destination with OnNavigatedListener • Use popTo for correct login flow behavior
  128. Don't include this fragment in the back stack

  129. popUpTo <fragment android:id="@+id/signUpFragment" ...> <action android:id="@+id/back_to_login_action" app:popUpTo="@+id/loginFragment" app:popUpToInclusive="false" /> </fragment>

    Removes all on back stack until reaching "loginFragment", not including "loginFragment"
  130. Pops any and all intermediate screens off back stack

  131. When login finishes - pop entire graph off the back

    stack to go back to whatever was before...
  132. popUpTo with Global Action <navigation android:id="@+id/login" app:startDestination="@id/loginFragment"> <action android:id="@+id/finish_login" app:popUpTo="@id/login"

    app:popUpToInclusive="true" /> <!-- Rest of Login NavGraph --> </navigation> Global action - pops to whatever was on the back stack right before
  133. Proprietary + Confidential Proprietary + Confidential Proprietary + Confidential Deep

    Linking
  134. Explicit Deep Linking

  135. Creating a Deep Link Pending Intent val pendingIntent = NavDeepLinkBuilder(context!!)

    .setGraph(R.navigation.navigation) .setDestination(R.id.gameWonFragment) .createPendingIntent()
  136. Sibling Sibling Sibling Start Destination

  137. Selected Destination Start Destination

  138. None
  139. Main Graph Start Destination Selected Destination Nested Graph Start Destination

  140. Implicit Deep Linking

  141. Creating our Link

  142. Deep Linking with an Argument <fragment android:id="@+id/user_profile" android:name="com.navsample.ui.UserProfile" android:label="fragment_user_profile" tools:layout="@layout/fragment_user_profile"

    > <argument android:name="selectedUser" app:type="string" /> <deepLink app:uri="http://www.navsample.com/userprofile/{selectedUser}/" /> </fragment>
  143. Deep Linking with an Argument <fragment android:id="@+id/user_profile" android:name="com.navsample.ui.UserProfile" android:label="fragment_user_profile" tools:layout="@layout/fragment_user_profile"

    > <argument android:name="selectedUser" app:type="string" /> <deepLink app:uri="http://www.navsample.com/userprofile/{selectedUser}/" /> </fragment>
  144. Deep Linking with an Argument <fragment android:id="@+id/user_profile" android:name="com.navsample.ui.UserProfile" android:label="fragment_user_profile" tools:layout="@layout/fragment_user_profile"

    > <argument android:name="selectedUser" app:type="string" /> <deepLink app:uri="http://www.navsample.com/userprofile/{selectedUser}/" /> </fragment>
  145. Adding the Intent Filter to the AndroidManifest <activity android:name=".ui.MainActivity"> <intent-filter>

    <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> <!-- Handle deep link into user profile --> <nav-graph android:value="@navigation/nav_graph" /> </activity>
  146. Proprietary + Confidential Proprietary + Confidential Proprietary + Confidential Instant

    Apps
  147. Instant Apps Native Android experience, no installation Downloads only needed

    modules
  148. base a b c instant instant installable installable Instant App

    Module Structure
  149. Instant App Module Structure base a b c installable nav_graph_a

    nav_graph_b nav_graph_c nav_graph_base None of these graphs should reference each other's ids
  150. Instant App Module Structure base a b c installable nav_graph_a

    nav_graph_b nav_graph_c nav_graph_base Installable main_nav_graph overrides base, but only for the installed app main_nav_graph main_nav_graph
  151. main_nav_graph.xml (installable) <navigation app:startDestination="@id/nav_graph_base"> <include app:graph="@navigation/nav_graph_base" /> <include app:graph="@navigation/nav_graph_a" />

    <include app:graph="@navigation/nav_graph_b" /> <include app:graph="@navigation/nav_graph_c" /> </navigation> Includes all feature graphs
  152. main_nav_graph.xml (base) <navigation app:startDestination="@id/base_dest"> <include app:graph="@navigation/nav_graph_base" /> <activity android:id="@id/a_dest" app:data="https://navigation.instantappsample.com/a"

    app:action="android.intent.action.VIEW"/> <!-- same deeplinks for b and c --> </navigation> Implicit Activity Deeplink for all features
  153. Instant App Module Structure base a b c installable nav_graph_a

    nav_graph_b nav_graph_c nav_graph_base To go from base to a,b and c in the installable app world... main_nav_graph main_nav_graph
  154. nav_graph_base.xml <navigation android:id="@+id/base_dest" app:startDestination="@id/base_fragment"> <fragment android:id="@+id/base_fragment" android:name="com.instantappsamples.navigation.base.BaseFragment"> <action android:id="@+id/action_base_to_a" app:destination="@+id/a_dest"

    /> <!--- similar for b and c ---> <deepLink app:uri="navigation.instantappsample.com/base" /> </fragment> </navigation> Action that triggers navigation Destination a_dest
  155. main_nav_graph.xml (installable) <navigation app:startDestination="@id/nav_graph_base"> <include app:graph="@navigation/nav_graph_base" /> <include app:graph="@navigation/nav_graph_a" />

    <include app:graph="@navigation/nav_graph_b" /> <include app:graph="@navigation/nav_graph_c" /> </navigation> Includes nav_graph_a
  156. nav_graph_a.xml <navigation android:id="@id/a_dest" app:startDestination="@id/a_fragment"> <fragment android:id="@+id/a_fragment" ...> <deepLink app:uri="navigation.instantappsample.com/a" />

    </fragment> <!-- graph A destinations --> </navigation> Destination a_dest (for installable app)
  157. Instant App Module Structure base a b c installable nav_graph_a

    nav_graph_b nav_graph_c nav_graph_base To go from base to a,b and c in the instant app world... main_nav_graph main_nav_graph
  158. nav_graph_base.xml <navigation android:id="@+id/base_dest" app:startDestination="@id/base_fragment"> <fragment android:id="@+id/base_fragment" android:name="com.instantappsamples.navigation.base.BaseFragment"> <action android:id="@+id/action_base_to_a" app:destination="@+id/a_dest"

    /> <!--- similar for b and c ---> <deepLink app:uri="navigation.instantappsample.com/base" /> </fragment> </navigation> Action that triggers navigation Destination a_dest
  159. main_nav_graph.xml (base) <navigation app:startDestination="@id/base_dest"> <include app:graph="@navigation/nav_graph_base" /> <activity android:id="@id/a_dest" app:data="https://navigation.instantappsample.com/a"

    app:action="android.intent.action.VIEW"/> <!-- similar actions for b and c --> </navigation> Implicit Activity Deeplink, called a_dest Uri https://navigation.instantappsample.com/a
  160. nav_graph_a.xml <navigation android:id="@id/a_dest" app:startDestination="@id/a_fragment"> <fragment android:id="@+id/a_fragment" ...> <deepLink app:uri="navigation.instantappsample.com/a" />

    </fragment> <!-- graph A destinations --> </navigation> Deep link destination for Uri https://navigation.instantappsample.com/a
  161. Instant App Module Structure base a b c installable nav_graph_a

    nav_graph_b nav_graph_c nav_graph_base To go from a to others features... main_nav_graph main_nav_graph
  162. nav_graph_b.xml <navigation ...> <fragment android:id="@+id/b_fragment" ...> <deepLink app:uri="navigation.instantappsample.com/b" /> <...>

    </fragment> <...> </navigation> Deep link destination for Uri https://navigation.instantappsample.com/b
  163. nav_graph_a.xml <navigation ...> <fragment android:id="@+id/a_fragment" ...> <deepLink app:uri="navigation.instantappsample.com/a" /> <action

    android:id="@+id/action_a_to_b" app:destination="@id/b_activity" /> </fragment> <!-- graph A destinations --> <activity android:id="@+id/b_activity" app:data="https://navigation.instantappsample.com/b" app:action="android.intent.action.VIEW" /> <!-- Similar destinations for base and c --> </navigation> Implicit Activity Deeplink, called b_Activity Uri https://navigation.instantappsample.com/a
  164. More Adventure! Documentation developer.android.com/topic/libraries/architecture/navigation/ DIY codelabs.developers.google.com/codelabs/android-navigation Samples github.com/googlesamples/android-sunflower github.com/googlesamples/android-architecture-components …

    NavigationBasicSample … GithubBrowserSample
  165. Proprietary + Confidential Proprietary + Confidential Proprietary + Confidential Thank

    You Dan Galpin @dagalpin