Upgrade to Pro — share decks privately, control downloads, hide ads and more …

[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!

Google Developers Group Lviv

October 13, 2018
Tweet

More Decks by Google Developers Group Lviv

Other Decks in Technology

Transcript

  1. Adventures in Navigation
    Dan Galpin
    @dagalpin

    View full-size slide

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

    View full-size slide

  3. Things weren't easy
    Fragment Transactions
    Deep Links Passing Arguments
    Error Prone Boilerplate
    Testing

    View full-size slide

  4. Things weren't consistent
    Deep Links
    Up and Back
    Navigation Drawer
    Bottom Navigation
    Menus

    View full-size slide

  5. Principles of Navigation

    View full-size slide

  6. Fixed Starting
    Destination

    View full-size slide

  7. Fixed Starting
    Destination

    View full-size slide

  8. Fixed Starting
    Destination*

    View full-size slide

  9. Up Navigation
    Traverses up the Hierarchy

    View full-size slide

  10. Up Navigation
    Traverses up the Hierarchy
    Parent Child

    View full-size slide

  11. Up Navigation
    Traverses up the Hierarchy
    Parent Child

    View full-size slide

  12. Up Navigation
    Traverses up the Hierarchy

    View full-size slide

  13. Up Navigation
    Traverses up the Hierarchy

    View full-size slide

  14. ==
    Within Your Task

    View full-size slide

  15. !=
    When Your Activity is Part of the Task of Another App

    View full-size slide

  16. Deep Linking

    View full-size slide

  17. Deep Linking

    View full-size slide

  18. Deep Linking

    View full-size slide

  19. Be Consistent

    View full-size slide

  20. Proprietary + Confidential
    Proprietary + Confidential
    Proprietary + Confidential
    A (quick) tour of the Navigation
    Component

    View full-size slide

  21. 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"

    View full-size slide

  22. 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"

    View full-size slide

  23. Navigation Graph
    (New Resource)
    NavHostFragment
    (Layout)
    NavController
    (Java/Kotlin)
    NavHost

    View full-size slide

  24. start destination
    destination
    destination
    destination
    destination
    destination
    destination
    destination

    View full-size slide

  25. action
    action
    action
    action
    action
    action
    action
    action
    action
    action

    View full-size slide

  26. Navigation Graph
    (New Resource)
    NavHostFragment
    (Layout)
    NavController
    (Java/Kotlin)
    NavHost

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  30. ● Visualizes navigation paths
    The Navigation Component

    View full-size slide

  31. The Navigation Component
    ● Visualizes navigation paths
    ● Defines navigation in one place

    View full-size slide

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

    View full-size slide

  33. The Navigation Component
    ● Visualizes navigation paths
    ● Defines navigation in one place
    ● Reduces nav pattern boilerplate
    ● Implements Material guidelines
    (including backstack)

    View full-size slide

  34. Getting Started

    View full-size slide

  35. Proprietary + Confidential
    Proprietary + Confidential
    Proprietary + Confidential
    General Guidance

    View full-size slide

  36. Fragment or
    Activity Destinations?

    View full-size slide

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

    View full-size slide

  38. Activities for Global
    Navigation
    NavHost
    Global navigation

    View full-size slide

  39. Fragments for Content
    NavHost
    Content

    View full-size slide

  40. 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

    View full-size slide

  41. Destinations or Actions?

    View full-size slide

  42. ● You can see them
    Use actions

    View full-size slide

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

    View full-size slide

  44. Use actions
    ● You can see them
    ● Your navigation graph will contain more
    information
    ● You can use safe args
    Argument passing

    View full-size slide

  45. Proprietary + Confidential
    Proprietary + Confidential
    Proprietary + Confidential
    Safe Args

    View full-size slide

  46. Safe Args
    Code Generation Plugin
    Uses your Navigation Graph to
    ensure type safety

    View full-size slide

  47. 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 ¯\_(ツ)_/¯

    View full-size slide

  48. 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

    View full-size slide

  49. navController.navigate(R.id.image_view)

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  53. val matchAction = RegisterDirections.
    actionRegisterToMatch("User3466")
    navController.navigate(matchAction)
    Example with argument without
    default
    R.id.action_register_to_match

    View full-size slide

  54. val args = MatchFragmentArgs.fromBundle(arguments)
    val opponent = args.opponent
    Match Fragment
    Fetching the arguments from
    the bundle

    View full-size slide

  55. 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)

    View full-size slide

  56. Proprietary + Confidential
    Proprietary + Confidential
    Proprietary + Confidential
    Navigation UI

    View full-size slide

  57. Up Button Overflow Menu Nav Drawer
    Bottom Nav

    View full-size slide

  58. Navigation Drawer
    Apps with five or more top-level destinations
    Apps with two or more levels of navigation hierarchy
    Quick navigation between unrelated destinations

    View full-size slide

  59. Start Destination
    Top Level Destinations

    View full-size slide

  60. Message Detail
    Back Stack

    View full-size slide

  61. Bottom Navigation
    Apps with three to five top-level destinations that
    need to be accessible anywhere in the app
    Mobile and Tablet only

    View full-size slide

  62. Start Destination

    View full-size slide

  63. Activity
    Outer Navigation Host Fragment
    Inner Navigation Host Fragment

    View full-size slide

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

    View full-size slide

  65. xmlns:android="http://schemas.android.com/apk/res/android">
    android:id="@+id/rulesFragment"
    android:icon="@drawable/rules"
    android:title="@string/rules" />
    android:id="@+id/aboutFragment"
    android:icon="@drawable/android"
    android:title="@string/about" />

    View full-size slide

  66. 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)
    }

    View full-size slide

  67. Setup Bottom Navigation
    // In onCreate of Activity
    // Setup the NavDrawer by passing the NavigationView to NavigationUI
    NavigationUI.setupWithNavController(binding.bottomNav, navController)

    View full-size slide

  68. The Up Button - Fragment + Toolbar
    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
    savedInstanceState: Bundle?): View? {
    ...
    val navController = findNavController()
    NavigationUI.setupWithNavController(binding.toolbar, navController)
    ...
    }

    View full-size slide

  69. 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)
    }

    View full-size slide

  70. 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

    View full-size slide

  71. 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;
    }
    });

    View full-size slide

  72. 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

    View full-size slide

  73. 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?

    View full-size slide

  74. NavHost NavHost

    View full-size slide

  75. 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?

    View full-size slide

  76. 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

    View full-size slide

  77. 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

    View full-size slide

  78. Proprietary + Confidential
    Proprietary + Confidential
    Proprietary + Confidential
    Shared Element Transitions

    View full-size slide

  79. Shared Elements

    View full-size slide

  80. 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

    View full-size slide

  81. 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

    View full-size slide

  82. Shared Element Transitions - Waiting for Images
    @BindingAdapter("sharedElementImageUrl")
    fun bindSharedImage(imageView: ImageView, url: String?) {
    Glide.with(fragment).load(url)
    .listener(object : RequestListener {
    override fun onResourceReady(_: Drawable?, _: Any?, _: Target?,
    _: DataSource?, _: Boolean): Boolean {
    fragment.startPostponedEnterTransition()
    return false
    }
    override fun onLoadFailed(e: GlideException?, _: Any?, _: Target?,
    _: Boolean): Boolean {
    fragment.startPostponedEnterTransition()
    return false
    }
    })
    .into(imageView)
    }

    View full-size slide

  83. Conditional Navigation

    View full-size slide

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

    View full-size slide

  85. 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

    View full-size slide

  86. Guide to App
    Architecture
    ● One way to do it, using Lifecycle
    libraries
    ● developer.android.com/jetpack/docs/g
    uide

    View full-size slide

  87. 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

    View full-size slide

  88. 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

    View full-size slide

  89. 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

    View full-size slide

  90. Event (Wrapper around LiveData to only handle once)
    open class Event(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
    }

    View full-size slide

  91. Fragment - User answers last question
    // Call a ViewModel method from the Fragment when user finishes the quiz
    binding.quizFinishedButton.setOnClickListener {
    viewModel.quizFinished()
    }

    View full-size slide

  92. ViewModel - Decide win or lose
    // ViewModel and LiveData
    val navigateToWin = MutableLiveData>()
    val navigateToGameOver = MutableLiveData>()
    fun quizFinished() {
    //Do logic with score, rules, etc to see if game was won
    }

    View full-size slide

  93. ViewModel with LiveData Event - Communicate
    Navigation
    // ViewModel and LiveData
    val navigateToWin = MutableLiveData>()
    val navigateToGameOver = MutableLiveData>()
    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)
    }
    }

    View full-size slide

  94. 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

    View full-size slide

  95. Only visible to logged in
    users
    Login Flow Example

    View full-size slide

  96. Login
    When I navigate or when my logged in state
    changes, check if I should be sent to the login
    flow.

    View full-size slide

  97. Login
    ● Use nested graph for Login flow

    View full-size slide

  98. Nested Graphs
    ● Good for sub-flows like login

    View full-size slide

  99. Nested Graphs
    ● Good for sub-flows like login
    ● Navigating to Nested Graphs will always
    send you to start destination
    Start Destination

    View full-size slide

  100. Login
    ● Use nested graph for Login flow
    ● Use global action

    View full-size slide

  101. Global Action
    ● Has a destination, but no starting point
    Normal action
    Global action

    View full-size slide

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

    View full-size slide

  103. 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

    View full-size slide

  104. android:id="@+id/main_navigation"
    app:startDestination="@id/fragmentA">










    View full-size slide

  105. android:id="@+id/main_navigation"
    app:startDestination="@id/fragmentA">









    android:id="@+id/login"
    app:startDestination="@id/loginFragment">



    Login nested graph

    View full-size slide

  106. android:id="@+id/main_navigation"
    app:startDestination="@id/fragmentA">









    android:id="@+id/login"
    app:startDestination="@id/loginFragment">


    android:id="@+id/action_global_login"
    app:destination="@id/login" />

    Global action

    View full-size slide

  107. Login
    ● Use nested graph for Login flow
    ● Use global action
    ● Use shared Activity ViewModel

    View full-size slide

  108. ViewModel ViewModel ViewModel ViewModel ViewModel
    Activity ViewModel
    for shared UI data

    View full-size slide

  109. LiveData
    Activity
    ViewModel
    currentDestinationId:
    LiveData
    isLoggedIn:
    LiveData
    navigateToLogin:
    LiveData>
    Just mentioned how
    to set this up
    Depends on what you're
    doing to implement login
    You can use an
    onNavigatedListener

    View full-size slide

  110. OnNavigatedListener
    ● Called as you arrive at a destination
    ○ Includes current destination
    ● Useful for updating Navigation UI
    ● Remember to remove listener

    View full-size slide

  111. OnNavigatedListener
    // In Activity onCreate
    navigationListener = NavController.OnNavigatedListener {
    controller, destination ->
    // Redirect navigation here
    }
    navController.addOnNavigatedListener(navigationListener)
    // In Activity onDestroy
    navController.removeOnNavigatedListener(navigationListener)
    Redirect navigation

    View full-size slide

  112. Login
    ● Use nested graph for Login flow
    ● Use global action
    ● Tracking current destination with
    OnNavigatedListener
    ● Use popTo for correct login flow
    behavior

    View full-size slide

  113. Don't include this fragment in
    the back stack

    View full-size slide

  114. popUpTo

    android:id="@+id/back_to_login_action"
    app:popUpTo="@+id/loginFragment"
    app:popUpToInclusive="false" />

    Removes all on back stack until reaching
    "loginFragment", not including
    "loginFragment"

    View full-size slide

  115. Pops any and all intermediate screens
    off back stack

    View full-size slide

  116. When login finishes - pop entire graph off the back stack to go
    back to whatever was before...

    View full-size slide

  117. popUpTo with Global Action
    android:id="@+id/login"
    app:startDestination="@id/loginFragment">
    android:id="@+id/finish_login"
    app:popUpTo="@id/login"
    app:popUpToInclusive="true" />


    Global action - pops to whatever was on
    the back stack right before

    View full-size slide

  118. Proprietary + Confidential
    Proprietary + Confidential
    Proprietary + Confidential
    Deep Linking

    View full-size slide

  119. Explicit Deep Linking

    View full-size slide

  120. Creating a Deep Link Pending Intent
    val pendingIntent = NavDeepLinkBuilder(context!!)
    .setGraph(R.navigation.navigation)
    .setDestination(R.id.gameWonFragment)
    .createPendingIntent()

    View full-size slide

  121. Sibling
    Sibling
    Sibling
    Start Destination

    View full-size slide

  122. Selected Destination
    Start Destination

    View full-size slide

  123. Main Graph Start
    Destination
    Selected Destination
    Nested Graph Start
    Destination

    View full-size slide

  124. Implicit Deep Linking

    View full-size slide

  125. Creating our Link

    View full-size slide

  126. Deep Linking with an Argument
    android:id="@+id/user_profile"
    android:name="com.navsample.ui.UserProfile"
    android:label="fragment_user_profile"
    tools:layout="@layout/fragment_user_profile" >
    android:name="selectedUser"
    app:type="string" />


    View full-size slide

  127. Deep Linking with an Argument
    android:id="@+id/user_profile"
    android:name="com.navsample.ui.UserProfile"
    android:label="fragment_user_profile"
    tools:layout="@layout/fragment_user_profile" >
    android:name="selectedUser"
    app:type="string" />


    View full-size slide

  128. Deep Linking with an Argument
    android:id="@+id/user_profile"
    android:name="com.navsample.ui.UserProfile"
    android:label="fragment_user_profile"
    tools:layout="@layout/fragment_user_profile" >
    android:name="selectedUser"
    app:type="string" />


    View full-size slide

  129. Adding the Intent Filter to the AndroidManifest








    View full-size slide

  130. Proprietary + Confidential
    Proprietary + Confidential
    Proprietary + Confidential
    Instant Apps

    View full-size slide

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

    View full-size slide

  132. base
    a b c
    instant
    instant
    installable
    installable
    Instant App Module Structure

    View full-size slide

  133. 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

    View full-size slide

  134. 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

    View full-size slide

  135. main_nav_graph.xml (installable)
    app:startDestination="@id/nav_graph_base">





    Includes all feature graphs

    View full-size slide

  136. main_nav_graph.xml (base)
    app:startDestination="@id/base_dest">

    android:id="@id/a_dest"
    app:data="https://navigation.instantappsample.com/a"
    app:action="android.intent.action.VIEW"/>


    Implicit Activity Deeplink for all
    features

    View full-size slide

  137. 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

    View full-size slide

  138. nav_graph_base.xml
    android:id="@+id/base_dest"
    app:startDestination="@id/base_fragment">
    android:id="@+id/base_fragment"
    android:name="com.instantappsamples.navigation.base.BaseFragment">
    android:id="@+id/action_base_to_a"
    app:destination="@+id/a_dest" />




    Action that triggers navigation
    Destination a_dest

    View full-size slide

  139. main_nav_graph.xml (installable)
    app:startDestination="@id/nav_graph_base">





    Includes nav_graph_a

    View full-size slide

  140. nav_graph_a.xml
    android:id="@id/a_dest"
    app:startDestination="@id/a_fragment">





    Destination a_dest (for installable app)

    View full-size slide

  141. 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

    View full-size slide

  142. nav_graph_base.xml
    android:id="@+id/base_dest"
    app:startDestination="@id/base_fragment">
    android:id="@+id/base_fragment"
    android:name="com.instantappsamples.navigation.base.BaseFragment">
    android:id="@+id/action_base_to_a"
    app:destination="@+id/a_dest" />




    Action that triggers navigation
    Destination a_dest

    View full-size slide

  143. main_nav_graph.xml (base)
    app:startDestination="@id/base_dest">

    android:id="@id/a_dest"
    app:data="https://navigation.instantappsample.com/a"
    app:action="android.intent.action.VIEW"/>


    Implicit Activity Deeplink, called a_dest
    Uri https://navigation.instantappsample.com/a

    View full-size slide

  144. nav_graph_a.xml
    android:id="@id/a_dest"
    app:startDestination="@id/a_fragment">





    Deep link destination for
    Uri https://navigation.instantappsample.com/a

    View full-size slide

  145. 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

    View full-size slide

  146. nav_graph_b.xml



    <...>

    <...>

    Deep link destination for
    Uri https://navigation.instantappsample.com/b

    View full-size slide

  147. nav_graph_a.xml



    android:id="@+id/action_a_to_b"
    app:destination="@id/b_activity" />


    android:id="@+id/b_activity"
    app:data="https://navigation.instantappsample.com/b"
    app:action="android.intent.action.VIEW" />


    Implicit Activity Deeplink, called b_Activity
    Uri https://navigation.instantappsample.com/a

    View full-size slide

  148. 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

    View full-size slide

  149. Proprietary + Confidential
    Proprietary + Confidential
    Proprietary + Confidential
    Thank You
    Dan Galpin
    @dagalpin

    View full-size slide