How to make your Android app compatible with Gesture Navigation using Kotlin

Last updated on: May 27, 2023

In this tutorial, I’m going to show you how to fix your app, when an Android 10 (or above) device uses Gesture Navigation, with some examples

Instead of the classic 2-3 button navigation bar, with the gesture navigation, we have to make some changes to look more beautiful.

Preparing the device/emulator for testing

Device

If you have an Android 10 device (or above) with a notch and you don’t have the Gesture navigation enabled, go to Settings > System > Gestures > System navigation and choose Gesture navigation

Emulator

Create a new emulator with Android Q (API 29) or above, choose Pixel 3 XL (or whatever pixel with a notch you like), and check the box Enable Device Frame.

Detecting if the Gesture Navigation is enabled

First, we need to detect when an Android 10 user has the Gesture navigation enabled.

To do that, we read the user’s settings system navigation option

private fun isGestureNavigationEnabled(): Boolean {
    val defaultNavigationMode = 0
    val gestureNavigationMode = 2

    return Settings.Secure.getInt(contentResolver, "navigation_mode", defaultNavigationMode) == gestureNavigationMode
}Code language: Kotlin (kotlin)

…and we use it like that:

if (isGestureNavigationEnabled()) {
    // Gesture Navigation is Enabled
} else {
    // 2-3 Buttons Navigation Bar is Enabled
}Code language: Swift (swift)

Please note that some users have reported that this method may not be the most secure way to detect it, as different devices may have varying numbers of gesture navigations. Unfortunately, Google has not yet provided a solution for this issue.

Fixing Full-Screen Activity

If you have a Full-Screen Activity and you wish to expand it across the entire screen:

class FullScreenActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_full_screen)

        // ...

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && isGestureNavigationEnabled()) {
            configureFullScreenMode()
        }
    }

    @RequiresApi(Build.VERSION_CODES.Q)
    private fun configureFullScreenMode() {
        // Extends the PhotoView in the whole screen
        window.addFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS)

        // Hides StatusBar and Navigation bar, you have to tap to appear
        // window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_IMMERSIVE or View.SYSTEM_UI_FLAG_FULLSCREEN or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION

        // Fixes the Full Screen black bar in screens with notch
        window.attributes.layoutInDisplayCutoutMode =
            WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
    }

    private fun isGestureNavigationEnabled(): Boolean {
        // Default navigation mode value
        val defaultNavigationMode = 0
        val gestureNavigationMode = 2

        return Settings.Secure.getInt(contentResolver, "navigation_mode", defaultNavigationMode) == gestureNavigationMode
    }
}Code language: Kotlin (kotlin)

Fixing Activity with RecyclerView

In an Activity with Views, like RecyclerView, you want to expand the RecyclerView under the Gesture Navigation Bar and avoid hiding the last item.

class RecyclerViewActivity : AppCompatActivity() {

    private var itemsArray = ArrayList<String>()
    private lateinit var adapter: RVAdapter

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_recycler_view)

        val recyclerView = findViewById<RecyclerView>(R.id.recycler_view)
        val recyclerViewContentId = findViewById<RelativeLayout>(R.id.recycler_view_content_id)

        // ...

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && isGestureNavigationEnabled()) {
            extendBackgroundUnderNavigationBar(recyclerView, recyclerViewContentId)
        }
    }

    // ...

    private fun extendBackgroundUnderNavigationBar(
        recyclerView: RecyclerView,
        recyclerViewContentId: RelativeLayout
    ) {
        // Disable clipping of the RecyclerView content when we use padding
        recyclerView.clipToPadding = false

        // Make the Gesture Navigation Bar transparent
        window.navigationBarColor = Color.TRANSPARENT

        // Expand the Views (RecyclerView) under the gesture navigation bar and toolbar
        window.decorView.systemUiVisibility =
            (View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_LAYOUT_STABLE)

        // Set padding for the Views (RecyclerView and Relative Layout) from the System Views (Gesture Navigation Bar, Toolbar)
        recyclerViewContentId.setOnApplyWindowInsetsListener { _, insets ->
            val topPadding = insets.systemWindowInsetTop
            val bottomPadding = insets.systemWindowInsetBottom
            recyclerViewContentId.setPadding(0, topPadding, 0, 0)
            recyclerView.setPadding(0, 0, 0, bottomPadding)
            insets.consumeSystemWindowInsets()
        }
    }

    private fun isGestureNavigationEnabled(): Boolean {
        val defaultNavigationMode = 0
        val gestureNavigationMode = 2

        return Settings.Secure.getInt(contentResolver, "navigation_mode", defaultNavigationMode) == gestureNavigationMode
    }

    // ...
}Code language: Kotlin (kotlin)
You can find the final project here

If you have any questionsplease feel free to leave a comment below

Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments