androidandroid-layoutandroid-statusbar

Why setting windowLightStatusBar ain't working programmatically using WindowInsetsControllerCompat#setAppearanceLightStatusBars()


I'm trying to create a launcher app, but i had problem when trying to change windowLightStatusBar programmatically on API 30 and the same code works perfectly fine bellow that, i hope if someone can help me out:)

Here is the full class source code:

public final class AppDrawer extends BottomSheetBehavior.BottomSheetCallback implements OnApplyWindowInsetsListener {

    private static boolean ALREADY_CREATED = false;

    private AppCompatActivity activity;
    private boolean dragging = false;
    private float radius;
    private int left, right, height, peekOver, oldState;
    private Rect displayRect = new Rect();
    private DisplayMetrics displayMetrics = new DisplayMetrics();
    private Insets navigationBarInsets = Insets.of(0, 0, 0, 0), cutoutInsets = navigationBarInsets;
    private WindowInsetsControllerCompat windowInsetsController;
    private Configuration oldConfiguration;
    private CardView cardView;
    private FrameLayout frameLayout;
    private BottomSheetBehavior<FrameLayout> bottomSheetBehavior;

    public AppDrawer(AppCompatActivity activity){
        if(ALREADY_CREATED) throw new IllegalArgumentException("The appDrawer object cannot be created more than once, clear the existing one from the memory first.");
        this.activity = activity;
        ALREADY_CREATED = true;
    }

    public void destroy(){
        activity = null;
        radius = 0.0f;
        left = right = height = peekOver = oldState = 0;
        displayRect = null;
        displayMetrics = null;
        navigationBarInsets = cutoutInsets = navigationBarInsets = null;
        windowInsetsController = null;
        oldConfiguration = null;
        cardView = null;
        frameLayout = null;
        bottomSheetBehavior = null;
        ALREADY_CREATED = false;
    }
    // WindowInsetControllerCompat is created and set from the activity that created this class
    public void setWindowInsetsController(@NonNull WindowInsetsControllerCompat windowInsetsController) { this.windowInsetsController = windowInsetsController; }

    public void initViews(View contentView){

        oldConfiguration = activity.getResources().getConfiguration();
        cardView = activity.findViewById(R.id.app_drawer_card_view);
        frameLayout = activity.findViewById(R.id.app_drawer_container);
        bottomSheetBehavior = BottomSheetBehavior.from(frameLayout);

        bottomSheetBehavior.addBottomSheetCallback(this);

        ViewCompat.setOnApplyWindowInsetsListener(contentView, this);

        updateVars();
    }

    @Override
    public WindowInsetsCompat onApplyWindowInsets(View v, WindowInsetsCompat insets) {
        if(dragging) return insets;
        cutoutInsets = insets.getInsets(WindowInsetsCompat.Type.displayCutout());
        navigationBarInsets = insets.getInsets(WindowInsetsCompat.Type.navigationBars());
        updateVars();
        if(bottomSheetBehavior.getState() == BottomSheetBehavior.STATE_COLLAPSED) frameLayout.setPadding(left + navigationBarInsets.left, 0, right + navigationBarInsets.right, 0);
        return insets;
    }

    public void setConfiguration(Configuration configuration) { this.oldConfiguration = configuration; }

    private void updateWindowInsetsBars(int state){
        // here is the one i used
        if (state != BottomSheetBehavior.STATE_COLLAPSED && (oldConfiguration.uiMode & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_NO) {
            windowInsetsController.setAppearanceLightNavigationBars(true);
            windowInsetsController.setAppearanceLightStatusBars(true);
        } else {
            windowInsetsController.setAppearanceLightNavigationBars(false);
            windowInsetsController.setAppearanceLightStatusBars(false);
        }

        // here is the one of @Zain answer
        if (state != BottomSheetBehavior.STATE_COLLAPSED && (oldConfiguration.uiMode & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_NO) {
            new WindowInsetsControllerCompat(activity.getWindow(), activity.getWindow().getDecorView()).setAppearanceLightStatusBars(true);
            new WindowInsetsControllerCompat(activity.getWindow(), activity.getWindow().getDecorView()).setAppearanceLightNavigationBars(true);
        } else {
            new WindowInsetsControllerCompat(activity.getWindow(), activity.getWindow().getDecorView()).setAppearanceLightStatusBars(false);
            new WindowInsetsControllerCompat(activity.getWindow(), activity.getWindow().getDecorView()).setAppearanceLightNavigationBars(false);
        }


    }

    private void updateCardViewState(float slideOffset){
        float a = 1.f - slideOffset;
        cardView.setRadius(radius * a);
        frameLayout.setPadding((int) ((left + navigationBarInsets.left) * a) , 0, (int) ((right + navigationBarInsets.right) * a), 0);
        FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) cardView.getLayoutParams();
        layoutParams.height = (int) ((displayRect.height() + navigationBarInsets.bottom + cutoutInsets.top + cutoutInsets.bottom) * slideOffset + height * a);
        Log.d("AppDrawer", "card height: " + layoutParams.height + ", screen height: " + displayRect.height());
        //cardView.setAlpha(Math.min(slideOffset * 1.1f, 1.0f));
        cardView.setLayoutParams(layoutParams);
    }

    private void updateDisplayMetrics(){
        WindowManager windowManager = this.activity.getWindowManager();
        windowManager.getDefaultDisplay().getMetrics(displayMetrics);
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.R){
            displayRect.set(windowManager.getCurrentWindowMetrics().getBounds());
            displayRect.bottom -= navigationBarInsets.bottom + cutoutInsets.top + cutoutInsets.bottom;
            displayRect.right -= navigationBarInsets.right + cutoutInsets.left + cutoutInsets.right;
        } else {
            displayRect.top = 0;
            displayRect.bottom = displayMetrics.heightPixels;
            displayRect.left = 0;
            displayRect.right = displayMetrics.widthPixels;
        }
    }

    private void updateVars(){
        updateDisplayMetrics();
        peekOver = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 10f, displayMetrics);
        radius = radius == 0 ? cardView.getRadius() : radius;
        left = left == 0 ? frameLayout.getPaddingLeft() : left;
        right = right == 0 ? frameLayout.getPaddingRight() : right;
        height = height == 0 ? cardView.getLayoutParams().height : height;
        bottomSheetBehavior.setPeekHeight(height + navigationBarInsets.bottom + peekOver);
        if(oldState != 0) updateCardViewState(oldState == BottomSheetBehavior.STATE_COLLAPSED ? 0.0f : 1.0f);
    }

    @Override
    public void onStateChanged(@NonNull View bottomSheet, int newState) {
        dragging = newState == BottomSheetBehavior.STATE_DRAGGING;
        updateWindowInsetsBars(newState);
        oldState = newState;
    }

    @Override
    public void onSlide(@NonNull View bottomSheet, float slideOffset) { updateCardViewState(slideOffset); }
}

Here you can see on API 30 that the navigationbar is white while the statusbar still black, even tho both are set to false but it doesn't effect the statusbar at all.

Screenshot-1 on API 30

Screenshot-2 on API 30

But here on API 29 (it works fine below that too) that they are both changes fine.

Screenshot-1 on API 29

Screenshot-2 on API 29


Solution

  • It is a platform bug. https://issuetracker.google.com/issues/180881870

    To fix this issue you should just update your androidx.core:core-ktx to version 1.8.0-alpha03 or higher.

    Pls refer to api changes for details https://developer.android.com/jetpack/androidx/releases/core#1.8.0-alpha03