[MUSIC LOGO]
NICK BEARMAN: Hi.
I'm Nick, and I'm a motion designer on the CIS UI team.
We're here to talk to you about the Android Back button
and gesture navigation.
Firstly, let's talk about how Back
evolved to understand the context of the change we're
introducing.
Home, Recents, and Back have been key actions
within Android for navigation, both within your app
and between different apps.
It first started as a hardware button
and evolved through the software Back button
to become gesture navigation.
The current approach requires no buttons, which
frees up screen real estate.
While we provide seamless animations
for going Home and entering Recents,
we don't yet do this for Back.
Our goal is to make Back as intuitive.
Android 13 marks the beginning of a multi-year investment
in gesture navigation to make it more predictable
and user friendly.
As part of this, we're updating the on-screen affordance
and improving haptics to give a consistent feel
across the system.
User research shows that people are most frustrated when System
Back unexpectedly returns them to the Launcher,
forcing them to reopen the app.
As a result, the first changes we are making
are designed to improve this experience.
API updates are planned for this release,
with prominent system improvements following soon
after.
However, we cannot provide users with these new interactions
without your help.
When users perform gestures to go Home or open Recents,
the system knows what is happening ahead of time.
This allows us to give intuitive feedback during the gestures
themselves.
When the user initiates a Back gesture,
the system doesn't know whether the app is handling it.
As a result, a suitable feed forward cannot be displayed.
Starting at Android 13, apps should tell the system
in advance if they will be handling Back events.
This supports a new ahead-of-time model
for dispatching System Back.
The system knows ahead of time, before the gesture
starts, if an application is handling System Back.
When this is not the case, the system
knows to take control and display feed-forward hints.
If we apply this to our Back-to-Home example,
knowing ahead of time allows the system
to preview the app dismiss, revealing the Launcher behind.
To do this, the system needs to know whether the next Back
action will be popping the in-app Back Stack
or returning to the Launcher.
As a result of these changes, users
will see what they are doing ahead of time, allowing
them to prevent false actions.
Now I'm going to hand it over to Shan, who will explain
how to adopt these changes.
SHAN HUANG: Amazing.
Thank you, Nick.
Hi.
I'm Shan, and I'm a software engineer on the System UI team.
To support this ahead-of-time model,
we're introducing a new API called onBackInvokedCallback,
which will be the cornerstone of the predictive Back navigation.
This interface has one main method, onBackInvoked,
which will be called instead of the legacy onBackPressed
methods from your activities and dialogues.
We will also stop sending key event or key callback
to applications using the new behavior.
To register one of these callbacks,
you need to get an onBackInvoked dispatcher
from activity or dialogue.
Once you have an instance of the dispatcher,
you can register a callback.
In doing so, you're telling the system ahead of time
that your code wants to handle System Back.
This way, when Back navigation happens,
your code will be called, and the system
will do nothing else.
On the other hand, if you do not provide a callback
or unregister your callback, the system
will take control and play these peaking animations
and will close your activity or dialogue when the gesture
or button press finishes.
To use this API, you need to adapt your app
to this new world of System Back.
In Android 13, we ask apps to explicitly opt in
to this new behavior.
Until you opt in, you won't be affected at all.
So the first thing to do is add enableOnBackInvoked equals
true in the Application tab of your Android manifest.
That is all you need if you don't provide custom
Back navigation in your app.
Note that with Android 14, the legacy behavior will entirely
be replaced by the ahead-of-time approach.
This flag also allows you to opt in early, even
before you target Android 13.
This is helpful if you want to verify that your Back
behavior will work correctly on an Android 13 device,
without opting into every single change in Android 13.
We recommend that you use the AndroidX Activity Library
and upgrade it to version 1.6.
With this update, libraries like AndroidX
recommends the Navigation component.
And usages of BackHandler and Jetpack Compose
are all instantly made compatible with Android 13,
while still being backwards compatible
with the previous versions of Android.
And that is all thanks to the ahead-of-time model
AndroidX Activity already had in its onBackPressed Dispatcher
API.
Each component activity and its subclasses
in FragmentActivity and AppCompatActivity
has an onBackPressed dispatcher.
The dispatcher automatically calls
your activities onBackPressed method
when you target previous versions of Android.
But on Android 13 and later, it will call the new platform
APIs.
We have recently expanded this to dialogues as well
with a component dialogue class, which
also has its own onBackPressed Dispatcher that
takes precedence when the dialogue is visible.
The duty of onBackPressed Dispatcher
is to maintain the set of callbacks
that want to handle system back and find and dispatch the Back
signal to the right callback.
There are two main styles of using these callbacks.
The first is to add your callback
as part of the creation of your component
and using this Enabled flag to turn the callback on and off
as the state of your component changes.
This style ensures that the nested components are always
added in the same order and therefore
are always considered by the dispatcher in the same order.
The dispatcher will just skip over any layers
that aren't enabled yet.
This also ensures that the innermost component gets
popped before the outer layers.
The other way to use this callback
is for temporary overlays.
In this case, you would wait to add your callback
until all of your enable conditions are met.
Notice that this breaks out of the default nested case
by ensuring that your callback is always the last one added.
This could be useful for, for example, a contextual action
bar.
Let's go through a more complicated example
with multiple callbacks.
Let's say that you're using the navigation component
with fragments to handle your back stack.
Here, your NavController does have
a back stack so its internal callback is enabled.
Each fragment has its own childFragmentManager
and could have a back stack.
But we're not using them yet.
So it is disabled.
Our topmost fragment is an entry form
and really doesn't want users to lose their data.
So it registered a callback specifically to open an,
"are you sure you want to cancel the submission dialog?"
The callback is disabled by default
because the user hasn't typed anything yet.
If a user tries to go back now, the NavigationController
is the topmost enabled callback and just takes the user back
without any dialogue interruption.
But once the user has started typing something,
the FragmentCallback is enabled and now it
would receive the callback for System Back.
The codes in that tall-most fragment
for adding the callback is straightforward.
We get the dispatcher from the activity,
and then we add the callback.
We provide a life-cycle owner.
Here, the fragment's life-cycle owner that ensures the callback
will only be triggered when the fragment's life
cycle is started and will automatically be unregistered
when the life cycle is destroyed,
preventing any memory leaks.
The trailing lambda is what is called to handle System Back
and where we show our dialogue.
Note that we passed in force for the default enable state
because, remember, for our case, we only
want to interrupt when the user has started typing something.
And that is where the do after task change listener comes in,
updating our isEnabled state when the task becomes not
empty.
If you have already started converting your app to Compose,
things are even easier for you, as all
of the onBackPressed Dispatcher API
has been wrapped into a single BackHandler
API that is part of the activity Compose artifact.
Jetpack Compose takes care of adding and removing
the callbacks for you.
All you need to do is control whether your BackHandler is
enabled and to implement your callback.
Again, both this code and the fragment codes
work on both Android 13 devices and older devices
once you upgrade AndroidX Activity to 1.6.
The AndroidX Activity APIs give you a backward-compatible way
to hook into the platform APIs and will continue
to be the future proof way to get new features
as we expand our work around Predictive Back.
If you are overriding on BackPressed in your activity,
you can start migrating that code into callbacks right now
and test the migration completely independently
from moving to Android 13.
The AndroidX Activity APIs give you those higher-level hooks
to handle System Back, but your apps
might also have lower-level components such as custom views
that are currently intercepting key callback to handle Back.
To move those lower-level components
to an ahead-of-time model, you need to use the platform APIs
directly.
Let's take a look at drawer layout
and how we integrated it using the new platform APIs.
To support pre-Android 13 devices,
drawer layout overrides views onKeyDown and onKeyUp methods
to handle System Back.
For views like drawer layout, even this wasn't always enough.
The view needs to be the currently focused view
to receive key event dispatches, which wasn't always the case.
This noticeably isn't the problem for the new platform
APIs.
You see that in the onKeyUp code,
there are two properties that control
whether drawer layout actually want to handle System Back,
whether the drawer is currently visible
and whether the lock mode is unlocked.
These are the signals that we need to migrate
to the ahead-of-time model.
First, at the class level, we'll create a framework
on BackInvokeCallback that will call closeDrawers method when
it is invoked.
We'll also create a separate flag
that keeps track of if we have already registered our callback
with the framework.
Next, we'll create a method that registers and unregisters
our callback based on the current state.
This is the method that we'll call ahead of time,
right when our state changes, rather
than waiting for the back signal to be fired.
We compare our new register states
with the old register state and call register or unregister.
This ensures our view's internal view of the world
is always in sync with framework's view of the world.
Lastly, we call our updateCallbackState
when the state changes.
For example, when we start dragging that drawer open
or when the log mode changes.
And with that, we're done.
Our drawer layout now supports the new ahead-of-time APIs
in Android 13.
We don't have to touch on onKeyDown or onKeyUp code.
The existing code will continue to be
called on previous versions of Android or when you opt out.
And with that, I'll hand over to Nick
to talk about our long-term plans for System Back.
NICK BEARMAN: Our team has been building out
animations for hero use cases, such as the one we show
when returning to the Launcher.
Animations within your application,
such as in-app transitions controlled by Jetpack Compose
or FragmentNavigation, won't show back previews just yet.
However, we're excited to kick off
the discussion between designers, developers,
and users on how these same types of peaking animations
could be applied everywhere.
If you want to learn more about System Back
and how to migrate your app to Android T,
you can check out our code lab.
We're also including a developer option
so you can test the Back-to-Home animation on your app.
Feel free to reach out to us on Twitter or Reddit
if you have any feedback or questions.
Thank you for listening.
[MUSIC LOGO]