Hi, everyone.
New in Android 11 is the ability for apps
to create seamless transitions between the on-screen keyboard
being open and then closed.
And it's all powered by lots of improvements
to the WindowInsets APIs in Android 11.
Here, you can see two examples of it in action.
It has been integrated into the Google search app,
as well as the Messages app on the right.
The days of the keyboard snapping instantly
onto the screen are hopefully gone.
Let's take a look at how you can add this sort of experience
to your apps.
So, there are three steps which I'll go through in this video.
The first step is by going edge-to-edge.
The second step is by reacting to inset animations.
And the third step is by apps taking control of
and driving inset animations,
if it makes sense for your app.
Let's start with step one, going edge-to-edge.
Last year, we introduced the concept of going edge-to-edge
as a way for apps to make the most of the new gestural navigation
in Android 10.
If you missed that talk from Android Dev Summit last year,
or the company blog post series, you can find the URLs below.
But as a quick recap,
going edge-to-edge results in your app drawn behind the system bars.
So going from this,
where the status and navigation bars have opaque backgrounds,
to this, where the app is drawn behind the system bars,
resulting in a blur between the lines of the app versus the system.
What has going edge-to-edge got to do with the keyboard?
Well, going edge-to-edge
is more than just drawing behind the system bars.
It's apps taking responsibility for handling those pieces of system UI
which might overlap with the app.
The two obvious examples are the status bar and the navigation bar,
which we mentioned earlier.
But we also have the on-screen keyboard,
or the IME, as it's sometimes referred to.
This is why going edge-to-edge is related to handling the keyboard.
It's just another piece of system UI to be aware of.
So, how do apps go edge-to-edge?
If you flash back to a section of our ADS talk from last year,
going edge-to-edge is made up of three tasks.
We're going to skip the first task,
because nothing has changed since last year.
But the guidance for two and three
have been updated with new changes in Android 11.
Let's take a look.
For the second step,
apps needed to use the systemUIVisibility API
with a whole bunch of flags to request to be laid out full-screen.
If you were using this API,
and you updated your compileSdkVersion to 30,
you'll have seen all of these APIs are now deprecated.
They've been replaced with a single functional window
called setDecorFitsSystemWindows.
And instead of the many flags, you're now passing a boolean.
False, if apps want to handle any system window fit themselves,
or true, if apps want the decor to instead,
which is the default value.
We also have a Jetpack version of the function available
in WindowCompat,
which was recently released in AndroidX Core.
That's the second step updated.
Let's take a look at the third step, avoiding overlaps with the system UI.
That can be summarized as using WindowInsets
to know where to move content to
to avoid conflicts with the system UI.
We take a look at the WindowInsets API before API 30,
the most common inset type to use would be the System Window Insets.
These cover the status and navigation bars,
but also the keyboard when it's open.
Previously, though, there was no way to distinguish
between the IME or the navigation bar when it's open.
It would just get one value.
To use WindowInsets,
you would typically add an OnApplyWindowInsetsListener to a view,
and then handle any new insets which are passed to it.
Here, we're fetching the system window insets
and then updating the view's pattern to match,
which is a very common use case, and which you'll find in a lot of apps.
But there are a number of other inset types available,
including the recently added GestureInsets.
Now again, on API 30, these have been deprecated.
Instead of having the individual types of properties, like this,
we now have some new APIs,
which allow you to query the insets for different types.
There are three functions:
getInsets,
which will return the visible insets for the given types,
getInsetsIgnoringVisibility,
which returns the insets regardless of whether they're visible or not,
and finally, isVisible,
which returns whether the given type is visible.
Now, we've just mentioned types a lot there.
Now, these are defined in the WindowInsets.Type class
as various functions, each returning an integer.
You can combine multiple types together,
using a bitwise ALL to create combined types,
which we'll see in a minute.
We also have a Jetpack version available in AndroidX Core.
If we go back to our example from before,
let's update it to the new APIs.
SystemWindowInsets becomes getInsets
with a combined type of systemBars and IME.
StableInsets becomes a call to getInsetsIgnoringVisibility
with the systemBars type.
And the final two become calls to getInsets
with a matching type.
Now, the keen-eyed among you
may have been looking at this list of types,
and they've been looking at one type in particular,
the IME type.
Well, we can find the answer this Stack Overflow question
from over 10 years ago now,
about how to check the visibility of the keyboard.
To get the current keyboard visibility,
we can fetch the root WindowInsets,
and then call the isVisible function, passing in the IME type.
Similarly, if you want to find out the height,
we can do that too.
If we instead want to listen to changes to the keyboard visibility,
we can use the normal OnApplyWindowInsetsListener,
and then use the same functions on the insets passed to the listener.
Since we're on a roll of answering Stack Overflow questions,
how about this one from 11 years ago of how to close the keyboard.
So here we're going to introduce another new API in Android 11
called windowInsetsController.
Apps can get access to a controller from any view,
and can then show and hide the keyboard as they wish,
by passing in the IME type.
But hiding and showing the keyboard isn't all that the controller can do.
Earlier, we mentioned that some of the system UI flags
have been deprecated, replaced with a new API.
Well, there are a number of other system UI flags available,
related to changing the system UI appearance or visibility.
And these have also been deprecated in API 30.
Now, instead of going through the migration for all of these flags,
we'll cover a few common scenarios and see how to update them.
Here you can see a drawing app which hides the system UI
to maximize the space available for drawing.
To implement that using the WindowInsetsController,
we use the Hide and Show functions like before,
but this time, passing in the systemBars type.
The app also uses immersive mode,
allowing the user to swipe the system bars back in.
To implement that using the WindowInsets controller,
we change the Hide and Show behavior to SHOW_BARS_BY_SWIPE.
Similarly, if you're using the Sticky Immersive mode instead,
this is implemented with the SHOW_TRANSIENT_BARS_BY_SWIPE
behavior instead.
These are exactly the same behaviors as before,
just hopefully with clearer names.
The next scenario is about the status bar content color.
So here you can see an example
where we have a dark status bar background with light content,
such as the time and the icons.
But if we instead want a light status bar background
with dark content,
we can use a WindowInsetsController to do that, too.
To do that, we can use the setSystemBarsAppearance function,
passing in the light status bar's value.
As you can see at the bottom,
you could also implement this in your theme instead,
if you know the value won't change.
Similarly, if you want to reset it
so that you have a dark status bar background,
you can pass in 0 to clear the value.
Now, unfortunately, we don't have any compat APIs this year,
but we are working on them.
Stay tuned for more information.
That's the first step done.
Now, we move on to inset animations.
So here we have a fake chat app.
When the user clicks on a text input to type reply,
the keyboard animates into place,
but the app snaps between the two states.
Now, this is a behavior you've seen on your devices for a while.
It's slowed down, so it's a lot more obvious.
Now let's look at what's possible in Android 11.
This time, when the user clicks on the text input,
the app moves with the keyboard to create a much more seamless experience.
The API which powers this in Android 11 is the new WindowInsets animation class,
which covers an animation involving insets.
Apps can listen to animation events for the Callback class,
which can be set on any view.
Let's take a look at the Callback class and the functions it provides.
Let's imagine that the user has clicked on the text input
and the system is about to start showing the keyboard.
At this point, onPrepare is called,
which allows apps to record any view state from the current layout.
After onPrepare, the normal WindowInsets will be dispatched in the view hierarchy
containing the end-state.
This means that your view's OnApplyWindowInsetsListener
will be called, which may cause the layout passed
to reflect the end-state.
The next step is onStart,
which is called the start of the animation.
This allows apps to record the view state of the target or the end-state.
Next up is the most important call, onProgress.
This is called every time the insets change in the animation.
In the case of the keyboard,
this will be as it slides in on screen.
And finally, onEnd is called when the animation is finished,
which you can use to clear up any old state.
So that's a walkthrough of the API.
Now let's create an example.
First, we'll assume that we have an onApplyWindowInsetsListener set,
which handles the IME being visible or not.
Now, let's add our WindowInsetsAnimation.Callback.
First, we'll override onPrepare,
and record the bottom coordinate of the view
before any layout changes have happened.
At this point, the end-state insets will be dispatched,
and our onApplyWindowInsetsListener called.
Our listener updates the padding of the Container view,
which results in the content being pushed up.
The user never sees this, as we'll soon find out.
Next, we have our onStart function,
which allows us to record the end position of the view.
But because we don't want the user to see the end state right now,
we shift the view back down to its original position
using translationY.
The user doesn't see a flicker, as the system guarantees
that the layout and onStart are called in the same frame.
Then finally, we override onProgress,
which allows us to update our view as the keyboard slides in.
We use translationY again,
interpolating between the start and the end states,
to make the view move in unison with the keyboard.
And that was step two, reacting to inset animations.
The third and final step is optional for a lot of apps,
and we think it's mostly useful for apps with lots of text input.
So it's just chat and communication apps.
Here we have our chat app again.
But this time, the app isn't only reacting to the keyboard
coming on the screen from focus in the input field.
This time, the app is taking control of the keyboard
and moving it based on the user dragging
and flinging the conversation list.
Now, this is actually three different things working together.
The first is that we need something to drive the animation.
Now, usually, this would be nested scrolling,
or maybe touch input.
Then, we need to react to WindowInsetsAnimations,
which we covered in the previous section.
And then finally, we're going to introduce another new API
from Android 11,
called the WindowInsetsAnimationController.
This API allows apps to take control and drive inset animations themselves.
The animation controller API is very generally powerful,
but it's also quite complex to use and explain in a video like this.
A full exploration of the API is beyond the scope of this video.
But our sample here shows how to implement it in full.
In that sample, we've written a number of helper classes
to simplify the implementation for certain use cases.
One of the classes is called SimpleImeAnimationController,
which is all about controlling the keyboard.
Let's take a step through the class to see how to use it.
The first function it provides is called startControlRequest.
And you would call that when you want to take control of the keyboard.
Next we have a function called isInsetAnimationInProgress,
which returns whether there is an inset animation in progress
at the moment.
Next, we have a function to insert or move the keyboard by pixel value.
And then finally, we have functions to finish
and cancel the animation.
So that's what SimpleImeAnimationController provides.
Now, let's use it to implement this.
We're going to create an onTouch listener,
which allows the user to bring in the keyboard off and on screen
by dragging the text input itself.
Here we have an onTouch function,
with some code blocks for the different action types.
From any MOVE actions,
we first check if there's an animation in progress.
If not, we start a request to take control.
But if there is an animation in progress,
we inset the keyboard by the amount that the user dragged by calling insetBy.
Finally, we hook up the finishing cancel calls
to the appropriate action types.
This is what it looks like in practice.
We can now drag on the text input,
which brings in the keyboard as the user drags up and down.
Those are the three steps to implementing keyboard animations in Android 11.
If you'd like to know more information about the APIs
that I spoke about in this video,
I'd highly recommend looking for the sampler,
as well as documentation on the various new APIs.
Thanks for watching.