Show transparent status bar and navigation bar under gesture navigation

Yes, UI designers are torturing me again.

In the case of gesture navigation, make the navigation bar transparent. In the case of three-button navigation, it is displayed normally, even if the interface is blocked.

What effect?

I was also drunk, this wonderful effect. The first picture is the content behind the occlusion, and the second picture is the content behind the no occlusion.

So how do I deal with it?

  1. Detect whether the current gesture navigation or three-button navigation

  1. Handle the UI abnormality caused by the current interface invading the navigation bar and status bar

  1. Regular transparent processing

import android.app.Activity
import android.content.ContentResolver
import android. content. Context
import android.content.res.Configuration
import android.content.res.Resources
import android.content.res.Resources.NotFoundException
import android.graphics.Color
import android.os.Build
import android.provider.Settings
import android.util.TypedValue
import android.view.*
import androidx.drawerlayout.widget.DrawerLayout

object PhoneBarManager {

    const val TAG = "PhoneBarManager"

    /**
     * Status bar height flag
     */
    private const val IMMERSION_STATUS_BAR_HEIGHT = "status_bar_height"

    fun setNavigationBarColor(window: Window, context: Context, color: Int) {
        //Prevent the size of the content area from changing when the system bar is hidden
        var uiFlags = View. SYSTEM_UI_FLAG_LAYOUT_STABLE

        //Set transparent status bar and navigation bar
        uiFlags = initBar(uiFlags, window, color)

        //apply flag
        window.decorView.setSystemUiVisibility(uiFlags)

        //Display transparent status bar and navigation bar
        showBar(window)

        setStatusBarLightMode(context, window)
    }

    fun setNavigationBarTrans(activity: Activity, isSupportActionBar: Boolean): Boolean {
        LogUtils.d(TAG, "actionBar?.isShowing --->${activity.actionBar?.isShowing}")
        //If there is a virtual button, no immersive processing will be done
        if (!isGestureNavigationMode(activity. contentResolver)) {
            return false
        }
        //Prevent the size of the content area from changing when the system bar is hidden
        var uiFlags = View. SYSTEM_UI_FLAG_LAYOUT_STABLE
        val window = activity.window

        //Set transparent status bar and navigation bar
        uiFlags = initBar(uiFlags, window, Color.TRANSPARENT)

        //apply flag
        window.decorView.setSystemUiVisibility(uiFlags)

        //Display transparent status bar and navigation bar
        showBar(window)

        // Solve the problem of status bar and layout overlapping
        fitsWindows(activity, isSupportActionBar)

        setStatusBarLightMode(activity, window)
        return true
    }

    private fun isGestureNavigationMode(content: ContentResolver?): Boolean {
        return Settings.Secure.getInt(content, "navigation_mode", 0) == 2
    }

    /**
     * Android 5.0 and above solve the problem of status bar and layout overlap
     * Fits windows above lollipop.
     */
    private fun fitsWindows(activity: Activity, isSupportActionBar: Boolean) {
        if (checkFitsSystemWindows(
                activity.window.decorView.findViewById(
                    android.R.id.content
                )
            )
        ) {
            setPadding(activity. window, 0, 0, 0, 0)
            return
        }
        var top = getInternalDimensionSize(activity, IMMERSION_STATUS_BAR_HEIGHT)
        //If you find that the status bar display is abnormal due to old items, such as more height or less height, you can directly adjust it here
        if (isSupportActionBar) {
            top + = getActionBarHeight(activity)
        }
        setPadding(activity. window, 0, top, 0, 0)
    }

    fun getActionBarHeight(activity: Activity): Int {
        val tv = TypedValue()
        return if (activity.getTheme().resolveAttribute(android.R.attr.actionBarSize, tv, true)) {
            TypedValue.complexToDimensionPixelSize(
                tv.data,
                activity. getResources(). getDisplayMetrics()
            )
        } else 0
    }

    private fun setPadding(window: Window, left: Int, top: Int, right: Int, bottom: Int) {
        val contentView = window.decorView.findViewById<View>(android.R.id.content)
        if (contentView != null) {
            contentView.setPadding(left, top, right, bottom)
        }
    }

    /**
     * Check if the layout root node uses the android:fitsSystemWindows="true" attribute
     * Check fits system windows boolean.
     *
     * @param view the view
     * @return the boolean
     */
    private fun checkFitsSystemWindows(view: View?): Boolean {
        if (view == null) {
            return false
        }
        if (view. fitsSystemWindows) {
            return true
        }
        if (view is ViewGroup) {
            val viewGroup = view
            var i = 0
            val count = viewGroup. childCount
            while (i < count) {
                val childView = viewGroup. getChildAt(i)
                if (childView is DrawerLayout) {
                    if (checkFitsSystemWindows(childView)) {
                        return true
                    }
                }
                if (childView. fitsSystemWindows) {
                    return true
                }
                i + +
            }
        }
        return false
    }

    private fun showBar(window: Window) {
        val contentView = window.decorView.findViewById<ViewGroup>(android.R.id.content)
        val controller: WindowInsetsController? = contentView. getWindowInsetsController()
        controller?.show(WindowInsets.Type.statusBars())
        controller?.show(WindowInsets.Type.navigationBars())
        controller?.systemBarsBehavior =
            WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
    }

    private fun initBar(uiFlags: Int, window: Window, color: Int): Int {
        //Get the default navigation bar color
        var uiFlags = uiFlags
        //Activity is displayed in full screen, but the status bar will not be hidden and covered, the status bar is still visible, and the top layout of the Activity will be covered by the status bar.
        uiFlags = uiFlags or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN

        //Activity is displayed in full screen, but the navigation bar will not be hidden and covered, the navigation bar is still visible, and the layout at the bottom of the Activity will be covered by the navigation bar.
        uiFlags = uiFlags or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION

        window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)

        window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION)

        //Need to set this to set the status bar and navigation bar color
        window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
        //Set status bar color
        window.setStatusBarContrastEnforced(false)
        window.setStatusBarColor(Color.TRANSPARENT)

        //Set the color of the navigation bar
        window.setNavigationBarContrastEnforced(false)
        window.setNavigationBarColor(color)
        return uiFlags
    }

    fun setStatusBarLightMode(activity: Context, window: Window) {
        val isFontColorDark = (activity.getResources().getConfiguration().uiMode
                and Configuration.UI_MODE_NIGHT_YES) !== 0

        val decorView: View = window. getDecorView()
        var originVisibility = decorView. systemUiVisibility
        // light mode, use black text
        if (!isFontColorDark & amp; & amp; originVisibility and View. SYSTEM_UI_FLAG_LIGHT_STATUS_BAR == 0) {
            originVisibility = originVisibility or View. SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
        }

        // dark mode, use white text
        if (isFontColorDark & amp; & amp; originVisibility and View. SYSTEM_UI_FLAG_LIGHT_STATUS_BAR != 0) {
            originVisibility = originVisibility xor View. SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
        }

        decorView.systemUiVisibility = originVisibility
    }

    private fun getInternalDimensionSize(context: Context, key: String): Int {
        val result = 0
        try {
            val resourceId = Resources.getSystem().getIdentifier(key, "dimen", "android")
            if (resourceId > 0) {
                val sizeOne = context.resources.getDimensionPixelSize(resourceId)
                val sizeTwo = Resources.getSystem().getDimensionPixelSize(resourceId)
                return if (sizeTwo >= sizeOne & amp; & amp; !(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q & amp; & amp;
                            key != IMMERSION_STATUS_BAR_HEIGHT)
                ) {
                    sizeTwo
                } else {
                    val densityOne = context.resources.displayMetrics.density
                    val densityTwo = Resources.getSystem().displayMetrics.density
                    val f = sizeOne * densityTwo / densityOne
                    (if (f >= 0) f + 0.5f else f - 0.5f).toInt()
                }
            }
        } catch (ignored: NotFoundException) {
            return 0
        }
        return result
    }
}

It’s really hard for me.

Come on, overtime dog~