4

I use NavHostFragment with BottomNavigationView with 5 tabs and fitsSystemWindows="true".

Description:

App has different app bar design for each fragment. For example, in 1st fragment app bar contains CollapsingToolbarLayout with background image which is drawn under status bar, and in 2nd fragment app bar contains only Toolbar and nothing is drawn under status bar. But to make UI not jumpy I just set all fragments layout to use fitsSystemWindows="true".

In first case, status bar background needs to be transparent to make image visible and it's icon's color should be white. But in the 2nd fragment app bar and status bar are white and icon's color should be dark.

I was using these two methods below to change status bar settings when switching between fragments.

private fun setStatusBarBackgroundColor(colorId: Int) {
    window.statusBarColor = ContextCompat.getColor(this, colorId)
}

@RequiresApi(Build.VERSION_CODES.M)
private fun setStatusBarIconsColor(isDark: Boolean) {   // this is causing the issue
    val oldFlags = window.decorView.systemUiVisibility
    var newFlags = oldFlags
    newFlags = if (isDark) {
        newFlags or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
    } else {
        newFlags and View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.inv()
    }
    if (newFlags != oldFlags) window.decorView.systemUiVisibility = newFlags
}

Problem:

However, on android Oreo and lower (23 >= API <= 27) I noticed that when navigating between fragments any of them can become blank at any time. After that, all of the next destinations (even navigating back) are blank as well. Only BottomNavigationView is visible and content of NavHostFragment is just blank. But in the logcat I see that fragments are created and data fetching is done, etc. No errors logs though

Prior to blank fragments if I swipe down to activate SwipeRefreshLayout and while it is refreshing I try to scroll, CollapsingToolbarLayout starts to behave very strangely. Scroll events won't be passed to RecyclerView until CollapsingToolbarLayout fully collapsed. Then RecyclerView will start to scroll but its content won't be drawn on all available vertical space but be hidden on top as if CollapsingToolbarLayout was expanded.

Removing invocation of method with SYSTEM_UI_FLAG_LIGHT_STATUS_BAR solves the problem. But I need to toggle the color of status bar icons.

Marat
  • 6,142
  • 6
  • 39
  • 67
  • I'm also struggling with this issue. Have you found a solution? The docs seem to state that FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS **must** be used in combination with SYSTEM_UI_FLAG_LIGHT_STATUS_BAR. Have you tried that? – guy.gc Mar 01 '20 at 22:07
  • 1
    @guy.gc I haven't found any solution yet. Temporarily disabled this feature and status bar icon colors are dark for now. I have set this in my parent AppTheme `true` so I didn't specify that flag in code. I will update my question if I find any solution when I will come back to this problem. If you find it earlier please share it here too. – Marat Mar 02 '20 at 09:43
  • Thanks for responding. We also didn't find a solution and had to change the UX because of this bug :\ – guy.gc Apr 06 '20 at 04:26
  • Hi, have you found any solution for this? – Alexei Artsimovich Sep 06 '21 at 19:11
  • 1
    @guy.gc I think I found a working solution that didn't break the UI so far – Marat Sep 07 '21 at 08:10
  • 1
    @AlexeiArtsimovich I posted an answer with solution that helped in my case – Marat Sep 07 '21 at 08:11
  • Thanks @Marat. What worked for us was to `View.post` the interactions with the statusBar. – guy.gc Sep 08 '21 at 05:54

1 Answers1

0

Android team added new API to make working with Window APIs easier. I'm not sure which version of core library has them but I believe starting from 1.5.0 these APIs are available. I'm using androidx.core:core-ktx:1.6.0.

Now setAppearanceLightStatusBars(boolean isLight) from InsetsController class allows to easily change the appearance of icons in status bar in runtime.

Solution:

WindowCompat.getInsetsController(window, window.decorView)?.isAppearanceLightStatusBars = isLightStatusBar

NOTE: on API 30 and later, calling this method has NO Effect if app has specified <item name="android:windowLightStatusBar">true/false</item> in the AppTheme in styles.xml. To be able to toggle status bar icons' color in runtime on API 30+, remove android:windowLightStatusBar from AppTheme

Under the hood, this method has 2 different implementations depending on Android OS version.

Internal implementation for API 23-29:

// internal method
public void setAppearanceLightStatusBars(boolean isLight) {
    if (isLight) {
        unsetWindowFlag(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
        setWindowFlag(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
        setSystemUiFlag(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
    } else {
        unsetSystemUiFlag(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
    }
}

Internal implementation for for API 30 and later:

// internal method
public void setAppearanceLightStatusBars(boolean isLight) {
    if (isLight) {
        mInsetsController.setSystemBarsAppearance(
            WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS,
            WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS);
    } else {
        mInsetsController.setSystemBarsAppearance(
            0,
            WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS);
    }
}
Marat
  • 6,142
  • 6
  • 39
  • 67