Slide 1

Slide 1 text

Going edge-to-edge Gesture Navigation

Slide 2

Slide 2 text

Chris Banes @chrisbanes @rohanscloud Rohan Shah

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

Which nav will Android use in the future? 3-button mode is required for Android devices Gestural nav may be offered as a polished, immersive experience, and can additionally be offered out-of-box

Slide 5

Slide 5 text

Why is this so important for apps? Gaining an edge on user experience given market desire for beautiful UI Handling conflicts with gestures for optimal UX

Slide 6

Slide 6 text

Making your app nav-ready 1. Make your UI edge-to-edge 2. Leverage insets for better UI 3. Override system gestures

Slide 7

Slide 7 text

What is
 edge-to-edge?

Slide 8

Slide 8 text

10:00

Slide 9

Slide 9 text

App bounds 10:00

Slide 10

Slide 10 text

No content

Slide 11

Slide 11 text

No content

Slide 12

Slide 12 text

10:00

Slide 13

Slide 13 text

10:00

Slide 14

Slide 14 text

10:00 Drawing behind the navigation bar Strongly recommended on Q Optional before Q

Slide 15

Slide 15 text

10:00

Slide 16

Slide 16 text

10:00

Slide 17

Slide 17 text

10:00 Drawing behind the status bar Recommended on Q Optional before Q

Slide 18

Slide 18 text

10:00

Slide 19

Slide 19 text

10:00

Slide 20

Slide 20 text

Going edge-to-edge

Slide 21

Slide 21 text

System will help From Q onwards, the system is responsible for protecting system buttons/handles

Slide 22

Slide 22 text

System will help From Q onwards, the system is responsible for protecting system buttons/handles

Slide 23

Slide 23 text

System will help From Q onwards, the system is responsible for protecting system buttons/handles Protection can be in the form of dynamic color adaptation

Slide 24

Slide 24 text

System will help From Q onwards, the system is responsible for protecting system buttons/handles Or the system can draw a translucent scrim Shown in button mode when targetSdkVersion >= 29

Slide 25

Slide 25 text

Going immersive Avoid overlaps with system UI Request to be laid out fullscreen Change system bar colors 1 2 3

Slide 26

Slide 26 text

<item name="android:navigationBarColor"> @android:color/transparent </item> values-v29/themes.xml Change system bar colors 1

Slide 27

Slide 27 text

<!-- 70% white --> <item name="android:navigationBarColor"> #B3FFFFFF </item> values/themes.xml Change system bar colors 1

Slide 28

Slide 28 text

<!-- 70% black --> <item name="android:navigationBarColor"> #B3000000 </item> values-night/themes.xml Change system bar colors 1

Slide 29

Slide 29 text

view.systemUiVisibility = // Tells the system that you wish to be laid out // as if the navigation bar was hidden View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or // Tells the system that you wish to be laid out at // the most extreme scenario of any other flags View.SYSTEM_UI_FLAG_LAYOUT_STABLE Request to be laid out fullscreen 2

Slide 30

Slide 30 text

view.systemUiVisibility = // Tells the system that you wish to be laid out // as if the navigation bar was hidden View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or // Optional, if we want you be laid out fullscreen, // behind the status bar View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or // Tells the system that you wish to be laid out at // the most extreme scenario of any other flags View.SYSTEM_UI_FLAG_LAYOUT_STABLE Request to be laid out fullscreen 2

Slide 31

Slide 31 text

Insets

Slide 32

Slide 32 text

What are insets? A collection of values which tell you how much to inset (move) content in by Which content to move, depends on the inset type and its purpose

Slide 33

Slide 33 text

I N S E T S System window Tells you where the system- ui is being displayed over your app Use to move clickable views away from edges

Slide 34

Slide 34 text

I N S E T S System window Tells you where the system- ui is being displayed over your app Use to move clickable views away from edges

Slide 35

Slide 35 text

System window I N S E T S Tells you where the system- ui is being displayed over your app Use to move clickable views away from edges

Slide 36

Slide 36 text

System window I N S E T S Tells you where the system- ui is being displayed over your app Use to move clickable views away from edges

Slide 37

Slide 37 text

System gesture Represents the areas of the window where system gestures take priority Includes the vertical edges for swiping back, and bottom edge for home Use to move draggable views away from edges New in Q I N S E T S

Slide 38

Slide 38 text

System gesture New in Q I N S E T S Represents the areas of the window where system gestures take priority Includes the vertical edges for swiping back, and bottom edge for home Use to move draggable views away from edges

Slide 39

Slide 39 text

System gesture New in Q I N S E T S Represents the areas of the window where system gestures take priority Includes the vertical edges for swiping back, and bottom edge for home Use to move draggable views away from edges

Slide 40

Slide 40 text

System gesture New in Q I N S E T S Represents the areas of the window where system gestures take priority Includes the vertical edges for swiping back, and bottom edge for home Use to move draggable views away from edges

Slide 41

Slide 41 text

System gesture New in Q I N S E T S Represents the areas of the window where system gestures take priority Includes the vertical edges for swiping back, and bottom edge for home Use to move draggable views away from edges

Slide 42

Slide 42 text

Mandatory 
 system gesture Subset of system gesture insets Defines areas which cannot be overridden by apps In Q, currently only used by the home gesture zone I N S E T S New in Q

Slide 43

Slide 43 text

System window insets Use to move clickable views away from edges System gesture insets Use to move draggable views away from edges Mandatory system gesture insets Use to check what gesture areas can not be excluded

Slide 44

Slide 44 text

So how do I actually handle insets? WindowInsets

Slide 45

Slide 45 text

ViewCompat.setOnApplyWindowInsetsListener(view) { v, insets -> // Do something with the insets // return the insets insets } Tip: always use the ViewCompat method

Slide 46

Slide 46 text

getSystemWindowInsets(): Insets getSystemGestureInsets(): Insets getMandatorySystemGestureInsets(): Insets getStableInsets(): Insets WindowInsetsCompat

Slide 47

Slide 47 text

getSystemWindowInsets(): Insets getSystemGestureInsets(): Insets getMandatorySystemGestureInsets(): Insets getStableInsets(): Insets WindowInsetsCompat data class Insets( left: Int, top: Int, right: Int, bottom: Int )

Slide 48

Slide 48 text

No content

Slide 49

Slide 49 text

<item name="android:navigationBarColor"> @android:color/transparent </item> values-v29/themes.xml

Slide 50

Slide 50 text

<item name="android:navigationBarColor"> @android:color/transparent </item> values-v29/themes.xml

Slide 51

Slide 51 text

view.systemUiVisibility = SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or SYSTEM_UI_FLAG_LAYOUT_STABLE

Slide 52

Slide 52 text

view.systemUiVisibility = SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or SYSTEM_UI_FLAG_LAYOUT_STABLE

Slide 53

Slide 53 text

view.systemUiVisibility = SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or SYSTEM_UI_FLAG_LAYOUT_STABLE

Slide 54

Slide 54 text

VC.setOnApplyWindowInsetsListener(bottomNav) { v, insets -> v.updatePadding( bottom = insets.systemWindowInsets.bottom ) insets }

Slide 55

Slide 55 text

VC.setOnApplyWindowInsetsListener(bottomNav) { v, insets -> v.updatePadding( bottom = insets.systemWindowInsets.bottom ) insets }

Slide 56

Slide 56 text

VC.setOnApplyWindowInsetsListener(bottomNav) { v, insets -> v.updatePadding( bottom = insets.systemWindowInsets.bottom ) insets }

Slide 57

Slide 57 text

VC.setOnApplyWindowInsetsListener(bottomNav) { v, insets -> v.updateLayoutParams( bottomMargin = insets.systemWindowInsets.bottom ) insets }

Slide 58

Slide 58 text

VC.setOnApplyWindowInsetsListener(bottomNav) { v, insets -> v.updateLayoutParams( bottomMargin = insets.systemWindowInsets.bottom ) insets }

Slide 59

Slide 59 text

No content

Slide 60

Slide 60 text

No content

Slide 61

Slide 61 text

Slide 62

Slide 62 text

After applying all of the steps...

Slide 63

Slide 63 text

VC.setOnApplyWindowInsetsListener(bottomBar) { v, insets -> v.updatePadding( bottom = insets.systemWindowInsets.bottom ) insets } Overwrites

Slide 64

Slide 64 text

VC.setOnApplyWindowInsetsListener(bottomBar) { v, insets -> v.updatePadding( bottom = bottomPad + insets.systemWindowInsets.bottom ) insets } val bottomPad = bottomBar.bottomPadding // 32dp

Slide 65

Slide 65 text

VC.setOnApplyWindowInsetsListener(bottomBar) { v, insets -> v.updatePadding( bottom = bottomPad + insets.systemWindowInsets.bottom ) insets } val bottomPad = bottomBar.bottomPadding

Slide 66

Slide 66 text

data class InitialPadding(
 val left: Int, val top: Int, val right: Int, val bottom: Int
 ) private fun recordInitialPaddingForView(view: View) = ... fun View.doOnApplyWindowInsets(f: (View, WindowInsets, InitialPadding) -> Unit) { // Create a snapshot of the view's padding state val initialPadding = recordInitialPaddingForView(this) // Set an OnApplyWindowInsetsListener which proxies to the given // lambda, passing in the original padding state ViewCompat.setOnApplyWindowInsetsListener(this) { v, insets -> f(v, insets, initialPadding) // Always return the insets, so that children can also use them insets } }

Slide 67

Slide 67 text

data class InitialPadding(
 val left: Int, val top: Int, val right: Int, val bottom: Int
 ) private fun recordInitialPaddingForView(view: View) = ... fun View.doOnApplyWindowInsets(f: (View, WindowInsets, InitialPadding) -> Unit) { // Create a snapshot of the view's padding state val initialPadding = recordInitialPaddingForView(this) // Set an OnApplyWindowInsetsListener which proxies to the given // lambda, passing in the original padding state ViewCompat.setOnApplyWindowInsetsListener(this) { v, insets -> f(v, insets, initialPadding) // Always return the insets, so that children can also use them insets } }

Slide 68

Slide 68 text

val right: Int, val bottom: Int
 ) private fun recordInitialPaddingForView(view: View) = ... fun View.doOnApplyWindowInsets(f: (View, WindowInsets, InitialPadding) -> Unit) { // Create a snapshot of the view's padding state val initialPadding = recordInitialPaddingForView(this) // Set an OnApplyWindowInsetsListener which proxies to the given // lambda, passing in the original padding state ViewCompat.setOnApplyWindowInsetsListener(this) { v, insets -> f(v, insets, initialPadding) // Always return the insets, so that children can also use them insets } }

Slide 69

Slide 69 text

bottomBar.doOnApplyWindowInsets { v, insets, initialPadding -> v.updatePadding( bottom = initialPadding.bottom + insets.systemWindowInsets.bottom ) insets }

Slide 70

Slide 70 text

- Nick Butcher

Slide 71

Slide 71 text

@BindingAdapter( "paddingLeftSystemWindowInsets", "paddingTopSystemWindowInsets", "paddingRightSystemWindowInsets", "paddingBottomSystemWindowInsets", requireAll = false ) fun applySystemWindows( view: View, applyLeft: Boolean, applyTop: Boolean, applyRight: Boolean, applyBottom: Boolean ) { view.doOnApplyWindowInsets { view, insets, padding -> val left = if (applyLeft) insets.systemWindowInsetLeft else val top = if (applyTop) insets.systemWindowInsetTop else 0

Slide 72

Slide 72 text

No more handling insets in code !

Slide 73

Slide 73 text

No content

Slide 74

Slide 74 text

Overriding gestures

Slide 75

Slide 75 text

What conflicts will apps face? Swiping in from the edges will go back or re-activate system gestures when in immersive mode. Any horizontally-draggable content that relies on gestures in the back zone Examples: sliders, seek bars, drag handles, etc.

Slide 76

Slide 76 text

What conflicts will apps face? Swiping in from the edges will go back or re-activate system gestures when in immersive mode. Any horizontally-draggable content that relies on gestures in the back zone Examples: sliders, seek bars, drag handles, etc.

Slide 77

Slide 77 text

Overriding gestures New API available to you for opting out a small part of the edge for app use Should be used extremely sparingly and tied to a draggable handle or visual affordance New in Q

Slide 78

Slide 78 text

View.setSystemGestureExclusionRects(List rects)

Slide 79

Slide 79 text

List exclusionRects; public void onLayout(...) { // Update rect bounds and the exclusionRects list setSystemGestureExclusionRects(exclusionRects); } CustomView.java

Slide 80

Slide 80 text

List exclusionRects; public void onDraw(Canvas canvas) { // Update rect bounds and the exclusionRects list setSystemGestureExclusionRects(exclusionRects); } CustomView.java

Slide 81

Slide 81 text

Restrictions on overriding gestures Apps can only opt out 200 dp of each edge If apps opt out of more, only the bottom 200 dp will be overridden for the app Requested

Slide 82

Slide 82 text

Restrictions on overriding gestures Apps can only opt out 200 dp of each edge If apps opt out of more, only the bottom 200 dp will be overridden for the app Honored Requested

Slide 83

Slide 83 text

Non-sticky immersive mode Apps can only opt out 200 dp of each edge from bringing in system bars and activating gestures

Slide 84

Slide 84 text

Non-sticky immersive mode Apps can only opt out 200 dp of each edge from bringing in system bars and activating gestures

Slide 85

Slide 85 text

Non-sticky immersive mode Apps can only opt out 200 dp of each edge from bringing in system bars and activating gestures

Slide 86

Slide 86 text

Non-sticky immersive mode Apps can only opt out 200 dp of each edge from bringing in system bars and activating gestures

Slide 87

Slide 87 text

Non-sticky immersive mode Apps can only opt out 200 dp of each edge from bringing in system bars and activating gestures

Slide 88

Slide 88 text

Sticky immersive mode By updating, apps can opt the entire edge out of bringing in system bars and reactivating gestures Once the gestures are active, the system will not respect any Back overrides

Slide 89

Slide 89 text

Sticky immersive mode By updating, apps can opt the entire edge out of bringing in system bars and reactivating gestures Once the gestures are active, the system will not respect any Back overrides

Slide 90

Slide 90 text

Sticky immersive mode By updating, apps can opt the entire edge out of bringing in system bars and reactivating gestures Once the gestures are active, the system will not respect any Back overrides

Slide 91

Slide 91 text

Sticky immersive mode By updating, apps can opt the entire edge out of bringing in system bars and reactivating gestures Once the gestures are active, the system will not respect any Back overrides

Slide 92

Slide 92 text

Sticky immersive mode By updating, apps can opt the entire edge out of bringing in system bars and reactivating gestures Once the gestures are active, the system will not respect any Back overrides

Slide 93

Slide 93 text

Sticky immersive mode By updating, apps can opt the entire edge out of bringing in system bars and reactivating gestures Once the gestures are active, the system will not respect any Back overrides

Slide 94

Slide 94 text

Sticky immersive mode Useful for single-purpose UIs that want to keep the user from accidentally going back Examples: games, drawing apps, etc.

Slide 95

Slide 95 text

Common scenarios

Slide 96

Slide 96 text

Scrolling views Content should be drawn behind the navigation bar When the user scrolls to the end, the content is drawn above the navigation bar S C E N A R I O S

Slide 97

Slide 97 text

Scrolling views Content should be drawn behind the navigation bar When the user scrolls to the end, the content is drawn above the navigation bar S C E N A R I O S

Slide 98

Slide 98 text

Scrolling views Content should be drawn behind the navigation bar When the user scrolls to the end, the content is drawn above the navigation bar S C E N A R I O S

Slide 99

Slide 99 text

Scrolling views Content should be drawn behind the navigation bar When the user scrolls to the end, the content is drawn above the navigation bar S C E N A R I O S

Slide 100

Slide 100 text

Scrolling views Common way to achieve this is through padding We’ll pad our scrolling view using the system window insets S C E N A R I O S

Slide 101

Slide 101 text

recyclerview.setOnApplyWindowInsetsListener { v, insets -> // Set the bottom padding so that the content bottom // is above the nav bar (y-axis) v.updatePadding(bottom = insets.systemWindowInsetBottom) // return the insets insets }

Slide 102

Slide 102 text

Enables our items to still be drawn within the parent’s padding

Slide 103

Slide 103 text

Bottom sheets For bottom sheets which collapse, ensure enough content is visible above the navigation bar to be dragged S C E N A R I O S

Slide 104

Slide 104 text

Bottom sheets For bottom sheets which collapse, ensure enough content is visible above the navigation bar to be dragged Use the system gesture insets to increase the peeking content height S C E N A R I O S

Slide 105

Slide 105 text

Bottom sheets For bottom sheets which collapse, ensure enough content is visible above the navigation bar to be dragged Use the system gesture insets to increase the peeking content height S C E N A R I O S

Slide 106

Slide 106 text

// Record the original peek height val origPeekHeight = behavior.peekHeight
 
 view.setOnApplyWindowInsetsListener { v, insets -> val gestureInsets = insets.getSystemGestureInsets() // Update the peek height so that it is above the navigation bar behavior.peekHeight = gestureInsets.bottom + origPeekHeight // return the insets insets }

Slide 107

Slide 107 text

Bottom sheets Remember, users can always swipe to go home Highlight draggable content to guide user S C E N A R I O S

Slide 108

Slide 108 text

Bottom navigation For bottom tabbed navigation, pad the bottom content in by the system window inset S C E N A R I O S

Slide 109

Slide 109 text

Bottom navigation For bottom tabbed navigation, pad the bottom content in by the system window inset S C E N A R I O S

Slide 110

Slide 110 text

Landscape S C E N A R I O S

Slide 111

Slide 111 text

Landscape S C E N A R I O S If you have content that starts near a vertical edge, think about padding all content in horizontally Make sure to test apps with button navigation too

Slide 112

Slide 112 text

rootView.setOnApplyWindowInsetsListener { v, insets -> // Set the left/right padding so that the content // is above the nav bar (y-axis) v.updatePadding( left = insets.systemWindowInsetLeft, right = insets.systemWindowInsetRight ) // return the insets insets }

Slide 113

Slide 113 text

Landscape S C E N A R I O S

Slide 114

Slide 114 text

Navigation drawers New swipe-when-peeking behavior in Q Beta 5 Works with all versions of DrawerLayout Update to DrawerLayout 1.1.0-alpha02 for best experience S C E N A R I O S

Slide 115

Slide 115 text

Carousels For ViewPagers and horizontal scrolling views, recommend to not exclude edges S C E N A R I O S

Slide 116

Slide 116 text

Carousels For ViewPagers and horizontal scrolling views, recommend to not exclude edges S C E N A R I O S

Slide 117

Slide 117 text

More support coming Raise bugs for difficult patterns issuetracker.google.com

Slide 118

Slide 118 text

TL;DR G E S T U R E N A V I G A T I O N Users will expect apps to work with all navigation modes Create immersive UIs by going edge-to-edge Proactively update Jetpack and MDC libraries for automatic support Only use gesture exclusion APIs if you really need them Let us know about troublesome scenarios in your apps

Slide 119

Slide 119 text

Chris Banes @chrisbanes @rohanscloud Rohan Shah Thanks!