/* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License */ package com.android.systemui.statusbar.phone; import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT; import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARENT; import android.content.Context; import android.graphics.Color; import android.graphics.Rect; import android.view.View; import com.android.internal.colorextraction.ColorExtractor.GradientColors; import com.android.systemui.Dumpable; import com.android.systemui.R; import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.statusbar.policy.BatteryController; import java.io.FileDescriptor; import java.io.PrintWriter; import javax.inject.Inject; import javax.inject.Singleton; /** * Controls how light status bar flag applies to the icons. */ @Singleton public class LightBarController implements BatteryController.BatteryStateChangeCallback, Dumpable { private static final float NAV_BAR_INVERSION_SCRIM_ALPHA_THRESHOLD = 0.1f; private final SysuiDarkIconDispatcher mStatusBarIconController; private final BatteryController mBatteryController; private BiometricUnlockController mBiometricUnlockController; private LightBarTransitionsController mNavigationBarController; private int mSystemUiVisibility; private int mFullscreenStackVisibility; private int mDockedStackVisibility; private boolean mFullscreenLight; private boolean mDockedLight; private int mLastStatusBarMode; private int mLastNavigationBarMode; private final Color mDarkModeColor; /** * Whether the navigation bar should be light factoring in already how much alpha the scrim has */ private boolean mNavigationLight; /** * Whether the flags indicate that a light status bar is requested. This doesn't factor in the * scrim alpha yet. */ private boolean mHasLightNavigationBar; /** * {@code true} if {@link #mHasLightNavigationBar} should be ignored and forcefully make * {@link #mNavigationLight} {@code false}. */ private boolean mForceDarkForScrim; private final Rect mLastFullscreenBounds = new Rect(); private final Rect mLastDockedBounds = new Rect(); private boolean mQsCustomizing; private boolean mDirectReplying; private boolean mNavbarColorManagedByIme; @Inject public LightBarController(Context ctx, DarkIconDispatcher darkIconDispatcher, BatteryController batteryController) { mDarkModeColor = Color.valueOf(ctx.getColor(R.color.dark_mode_icon_color_single_tone)); mStatusBarIconController = (SysuiDarkIconDispatcher) darkIconDispatcher; mBatteryController = batteryController; mBatteryController.addCallback(this); } public void setNavigationBar(LightBarTransitionsController navigationBar) { mNavigationBarController = navigationBar; updateNavigation(); } public void setBiometricUnlockController( BiometricUnlockController biometricUnlockController) { mBiometricUnlockController = biometricUnlockController; } public void onSystemUiVisibilityChanged(int fullscreenStackVis, int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds, boolean sbModeChanged, int statusBarMode, boolean navbarColorManagedByIme) { int oldFullscreen = mFullscreenStackVisibility; int newFullscreen = (oldFullscreen & ~mask) | (fullscreenStackVis & mask); int diffFullscreen = newFullscreen ^ oldFullscreen; int oldDocked = mDockedStackVisibility; int newDocked = (oldDocked & ~mask) | (dockedStackVis & mask); int diffDocked = newDocked ^ oldDocked; if ((diffFullscreen & View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR) != 0 || (diffDocked & View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR) != 0 || sbModeChanged || !mLastFullscreenBounds.equals(fullscreenStackBounds) || !mLastDockedBounds.equals(dockedStackBounds)) { mFullscreenLight = isLight(newFullscreen, statusBarMode, View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR); mDockedLight = isLight(newDocked, statusBarMode, View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR); updateStatus(fullscreenStackBounds, dockedStackBounds); } mFullscreenStackVisibility = newFullscreen; mDockedStackVisibility = newDocked; mLastStatusBarMode = statusBarMode; mNavbarColorManagedByIme = navbarColorManagedByIme; mLastFullscreenBounds.set(fullscreenStackBounds); mLastDockedBounds.set(dockedStackBounds); } public void onNavigationVisibilityChanged(int vis, int mask, boolean nbModeChanged, int navigationBarMode, boolean navbarColorManagedByIme) { int oldVis = mSystemUiVisibility; int newVis = (oldVis & ~mask) | (vis & mask); int diffVis = newVis ^ oldVis; if ((diffVis & View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR) != 0 || nbModeChanged) { boolean last = mNavigationLight; mHasLightNavigationBar = isLight(vis, navigationBarMode, View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR); mNavigationLight = mHasLightNavigationBar && (mDirectReplying && mNavbarColorManagedByIme || !mForceDarkForScrim) && !mQsCustomizing; if (mNavigationLight != last) { updateNavigation(); } } mSystemUiVisibility = newVis; mLastNavigationBarMode = navigationBarMode; mNavbarColorManagedByIme = navbarColorManagedByIme; } private void reevaluate() { onSystemUiVisibilityChanged(mFullscreenStackVisibility, mDockedStackVisibility, 0 /* mask */, mLastFullscreenBounds, mLastDockedBounds, true /* sbModeChange*/, mLastStatusBarMode, mNavbarColorManagedByIme); onNavigationVisibilityChanged(mSystemUiVisibility, 0 /* mask */, true /* nbModeChanged */, mLastNavigationBarMode, mNavbarColorManagedByIme); } public void setQsCustomizing(boolean customizing) { if (mQsCustomizing == customizing) return; mQsCustomizing = customizing; reevaluate(); } /** * Sets whether the direct-reply is in use or not. * @param directReplying {@code true} when the direct-reply is in-use. */ public void setDirectReplying(boolean directReplying) { if (mDirectReplying == directReplying) return; mDirectReplying = directReplying; reevaluate(); } public void setScrimState(ScrimState scrimState, float scrimBehindAlpha, GradientColors scrimInFrontColor) { boolean forceDarkForScrimLast = mForceDarkForScrim; // For BOUNCER/BOUNCER_SCRIMMED cases, we assume that alpha is always below threshold. // This enables IMEs to control the navigation bar color. // For other cases, scrim should be able to veto the light navigation bar. mForceDarkForScrim = scrimState != ScrimState.BOUNCER && scrimState != ScrimState.BOUNCER_SCRIMMED && scrimBehindAlpha >= NAV_BAR_INVERSION_SCRIM_ALPHA_THRESHOLD && !scrimInFrontColor.supportsDarkText(); if (mHasLightNavigationBar && (mForceDarkForScrim != forceDarkForScrimLast)) { reevaluate(); } } private boolean isLight(int vis, int barMode, int flag) { boolean isTransparentBar = (barMode == MODE_TRANSPARENT || barMode == MODE_LIGHTS_OUT_TRANSPARENT); boolean light = (vis & flag) != 0; return isTransparentBar && light; } private boolean animateChange() { if (mBiometricUnlockController == null) { return false; } int unlockMode = mBiometricUnlockController.getMode(); return unlockMode != BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING && unlockMode != BiometricUnlockController.MODE_WAKE_AND_UNLOCK; } private void updateStatus(Rect fullscreenStackBounds, Rect dockedStackBounds) { boolean hasDockedStack = !dockedStackBounds.isEmpty(); // If both are light or fullscreen is light and there is no docked stack, all icons get // dark. if ((mFullscreenLight && mDockedLight) || (mFullscreenLight && !hasDockedStack)) { mStatusBarIconController.setIconsDarkArea(null); mStatusBarIconController.getTransitionsController().setIconsDark(true, animateChange()); } // If no one is light or the fullscreen is not light and there is no docked stack, // all icons become white. else if ((!mFullscreenLight && !mDockedLight) || (!mFullscreenLight && !hasDockedStack)) { mStatusBarIconController.getTransitionsController().setIconsDark( false, animateChange()); } // Not the same for every stack, magic! else { Rect bounds = mFullscreenLight ? fullscreenStackBounds : dockedStackBounds; if (bounds.isEmpty()) { mStatusBarIconController.setIconsDarkArea(null); } else { mStatusBarIconController.setIconsDarkArea(bounds); } mStatusBarIconController.getTransitionsController().setIconsDark(true, animateChange()); } } private void updateNavigation() { if (mNavigationBarController != null) { mNavigationBarController.setIconsDark( mNavigationLight, animateChange()); } } @Override public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) { } @Override public void onPowerSaveChanged(boolean isPowerSave) { reevaluate(); } @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { pw.println("LightBarController: "); pw.print(" mSystemUiVisibility=0x"); pw.print( Integer.toHexString(mSystemUiVisibility)); pw.print(" mFullscreenStackVisibility=0x"); pw.print( Integer.toHexString(mFullscreenStackVisibility)); pw.print(" mDockedStackVisibility=0x"); pw.println( Integer.toHexString(mDockedStackVisibility)); pw.print(" mFullscreenLight="); pw.print(mFullscreenLight); pw.print(" mDockedLight="); pw.println(mDockedLight); pw.print(" mLastFullscreenBounds="); pw.print(mLastFullscreenBounds); pw.print(" mLastDockedBounds="); pw.println(mLastDockedBounds); pw.print(" mNavigationLight="); pw.print(mNavigationLight); pw.print(" mHasLightNavigationBar="); pw.println(mHasLightNavigationBar); pw.print(" mLastStatusBarMode="); pw.print(mLastStatusBarMode); pw.print(" mLastNavigationBarMode="); pw.println(mLastNavigationBarMode); pw.print(" mForceDarkForScrim="); pw.print(mForceDarkForScrim); pw.print(" mQsCustomizing="); pw.print(mQsCustomizing); pw.print(" mDirectReplying="); pw.println(mDirectReplying); pw.print(" mNavbarColorManagedByIme="); pw.println(mNavbarColorManagedByIme); pw.println(); LightBarTransitionsController transitionsController = mStatusBarIconController.getTransitionsController(); if (transitionsController != null) { pw.println(" StatusBarTransitionsController:"); transitionsController.dump(fd, pw, args); pw.println(); } if (mNavigationBarController != null) { pw.println(" NavigationBarTransitionsController:"); mNavigationBarController.dump(fd, pw, args); pw.println(); } } }