In this episode you’ll use: DecorFitsSystemWindows, OnApplyWindowInsetsListener. API’s to implement a new feature.
In order to animate the keyboard, you first need to set your app as fullscreen because it’s part of the system UI. Until Android 11 this could be done by setting the systemUiVisibility in the window decorView:
window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or
View.SYSTEM_UI_FLAG_FULLSCREEN
Where the application will be full screen, meaning that it will overlay the status and navigation bars.
This has always been tricky to find the right combination of flags, so, fortunately, it has been deprecated in favor of:
WindowCompat.setDecorFitsSystemWindows(Window, Boolean)
The first parameter corresponds to the application window and the second if the app is going to handle the system windows or not. Open the project and go to MainActivity.kt. Before the call to super.onCreate(savedInstanceState) add:
WindowCompat.setDecorFitsSystemWindows(window, !isAtLeastAndroid11())
You need to import WindowCompat from the androidx.core.view.WindowCompat This feature is only available on Android 11 and afterward, so your app should only be set as full screen on these versions. On lower versions, it should be the system’s responsibility to control the app windows. It should be set as false in these scenarios.
To visually see these differences run the app on a device with Android 10 and in another one with Android 11. In Android 11 the app occupies the entire screen as you’ve requested, so it’s time to handle these boundaries. Open the RWCompat11.kt and update the setUiWindowInsets:
private var posTop = 0
private var posBottom = 0
fun setUiWindowInsets() {
ViewCompat.setOnApplyWindowInsetsListener(container) { _, insets ->
posTop = insets.getInsets(WindowInsetsCompat.Type.systemBars()).top
posBottom = insets.getInsets(WindowInsetsCompat.Type.ime()).bottom
val navBar = insets.getInsets(WindowInsetsCompat.Type.navigationBars())
if (posBottom < navBar.bottom) {
posBottom = navBar.bottom
}
container.updateLayoutParams<ViewGroup.MarginLayoutParams> {
updateMargins(
top = posTop,
bottom = posBottom)
}
insets
}
}
Start by defining the SetOnApplyWindowInsetsListener. This listener is called with two parameters: View and WindowInsets. In these scenarios only the second one is required, so let’s change the field name to _ since it’s not going to be used.
Currently, the app is going over the status bar and the bottom navigation bar, so it’s necessary that you calculate these two values. Add two fields outside of the function scope to hold these values.
They need to be updated inside the SetOnApplyWindowInsetsListener otherwise, you might get the wrong value for the system bar’s height. These values are set the first time the listener is called by retrieving the top height from the systemBars for the posTop and the bottom for posBottom.
After these two values are set, it’s time to update the view container to its new locations. This is done by calling updateMargins with these two new values that will rearrange the UI to be within the screen limits.
Let’s compile and run the app and see how everything is now in the right place! Now that the app is open, don’t forget to add the episode name to the list: 04: Adjust the System Windows
See you in the next episode!