1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License 15 */ 16 17 package com.android.systemui.statusbar.phone; 18 19 import android.app.WallpaperColors; 20 import android.content.Context; 21 import android.graphics.Color; 22 import android.graphics.Rect; 23 import android.view.View; 24 25 import com.android.internal.colorextraction.ColorExtractor.GradientColors; 26 import com.android.systemui.Dependency; 27 import com.android.systemui.Dumpable; 28 import com.android.systemui.R; 29 import com.android.systemui.statusbar.policy.BatteryController; 30 import com.android.systemui.statusbar.policy.DarkIconDispatcher; 31 32 import java.io.FileDescriptor; 33 import java.io.PrintWriter; 34 35 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT; 36 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARENT; 37 38 /** 39 * Controls how light status bar flag applies to the icons. 40 */ 41 public class LightBarController implements BatteryController.BatteryStateChangeCallback, Dumpable { 42 43 private static final float NAV_BAR_INVERSION_SCRIM_ALPHA_THRESHOLD = 0.1f; 44 45 private final DarkIconDispatcher mStatusBarIconController; 46 private final BatteryController mBatteryController; 47 private FingerprintUnlockController mFingerprintUnlockController; 48 49 private LightBarTransitionsController mNavigationBarController; 50 private int mSystemUiVisibility; 51 private int mFullscreenStackVisibility; 52 private int mDockedStackVisibility; 53 private boolean mFullscreenLight; 54 private boolean mDockedLight; 55 private int mLastStatusBarMode; 56 private int mLastNavigationBarMode; 57 private final Color mDarkModeColor; 58 59 /** 60 * Whether the navigation bar should be light factoring in already how much alpha the scrim has 61 */ 62 private boolean mNavigationLight; 63 64 /** 65 * Whether the flags indicate that a light status bar is requested. This doesn't factor in the 66 * scrim alpha yet. 67 */ 68 private boolean mHasLightNavigationBar; 69 70 /** 71 * {@code true} if {@link #mHasLightNavigationBar} should be ignored and forcefully make 72 * {@link #mNavigationLight} {@code false}. 73 */ 74 private boolean mForceDarkForScrim; 75 76 private final Rect mLastFullscreenBounds = new Rect(); 77 private final Rect mLastDockedBounds = new Rect(); 78 private boolean mQsCustomizing; 79 LightBarController(Context ctx)80 public LightBarController(Context ctx) { 81 mDarkModeColor = Color.valueOf(ctx.getColor(R.color.dark_mode_icon_color_single_tone)); 82 mStatusBarIconController = Dependency.get(DarkIconDispatcher.class); 83 mBatteryController = Dependency.get(BatteryController.class); 84 mBatteryController.addCallback(this); 85 } 86 setNavigationBar(LightBarTransitionsController navigationBar)87 public void setNavigationBar(LightBarTransitionsController navigationBar) { 88 mNavigationBarController = navigationBar; 89 updateNavigation(); 90 } 91 setFingerprintUnlockController( FingerprintUnlockController fingerprintUnlockController)92 public void setFingerprintUnlockController( 93 FingerprintUnlockController fingerprintUnlockController) { 94 mFingerprintUnlockController = fingerprintUnlockController; 95 } 96 onSystemUiVisibilityChanged(int fullscreenStackVis, int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds, boolean sbModeChanged, int statusBarMode)97 public void onSystemUiVisibilityChanged(int fullscreenStackVis, int dockedStackVis, 98 int mask, Rect fullscreenStackBounds, Rect dockedStackBounds, boolean sbModeChanged, 99 int statusBarMode) { 100 int oldFullscreen = mFullscreenStackVisibility; 101 int newFullscreen = (oldFullscreen & ~mask) | (fullscreenStackVis & mask); 102 int diffFullscreen = newFullscreen ^ oldFullscreen; 103 int oldDocked = mDockedStackVisibility; 104 int newDocked = (oldDocked & ~mask) | (dockedStackVis & mask); 105 int diffDocked = newDocked ^ oldDocked; 106 if ((diffFullscreen & View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR) != 0 107 || (diffDocked & View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR) != 0 108 || sbModeChanged 109 || !mLastFullscreenBounds.equals(fullscreenStackBounds) 110 || !mLastDockedBounds.equals(dockedStackBounds)) { 111 112 mFullscreenLight = isLight(newFullscreen, statusBarMode, 113 View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR); 114 mDockedLight = isLight(newDocked, statusBarMode, View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR); 115 updateStatus(fullscreenStackBounds, dockedStackBounds); 116 } 117 118 mFullscreenStackVisibility = newFullscreen; 119 mDockedStackVisibility = newDocked; 120 mLastStatusBarMode = statusBarMode; 121 mLastFullscreenBounds.set(fullscreenStackBounds); 122 mLastDockedBounds.set(dockedStackBounds); 123 } 124 onNavigationVisibilityChanged(int vis, int mask, boolean nbModeChanged, int navigationBarMode)125 public void onNavigationVisibilityChanged(int vis, int mask, boolean nbModeChanged, 126 int navigationBarMode) { 127 int oldVis = mSystemUiVisibility; 128 int newVis = (oldVis & ~mask) | (vis & mask); 129 int diffVis = newVis ^ oldVis; 130 if ((diffVis & View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR) != 0 131 || nbModeChanged) { 132 boolean last = mNavigationLight; 133 mHasLightNavigationBar = isLight(vis, navigationBarMode, 134 View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR); 135 mNavigationLight = mHasLightNavigationBar && !mForceDarkForScrim && !mQsCustomizing; 136 if (mNavigationLight != last) { 137 updateNavigation(); 138 } 139 } 140 mSystemUiVisibility = newVis; 141 mLastNavigationBarMode = navigationBarMode; 142 } 143 reevaluate()144 private void reevaluate() { 145 onSystemUiVisibilityChanged(mFullscreenStackVisibility, 146 mDockedStackVisibility, 0 /* mask */, mLastFullscreenBounds, mLastDockedBounds, 147 true /* sbModeChange*/, mLastStatusBarMode); 148 onNavigationVisibilityChanged(mSystemUiVisibility, 0 /* mask */, true /* nbModeChanged */, 149 mLastNavigationBarMode); 150 } 151 setQsCustomizing(boolean customizing)152 public void setQsCustomizing(boolean customizing) { 153 if (mQsCustomizing == customizing) return; 154 mQsCustomizing = customizing; 155 reevaluate(); 156 } 157 setScrimState(ScrimState scrimState, float scrimBehindAlpha, GradientColors scrimInFrontColor)158 public void setScrimState(ScrimState scrimState, float scrimBehindAlpha, 159 GradientColors scrimInFrontColor) { 160 boolean forceDarkForScrimLast = mForceDarkForScrim; 161 // For BOUNCER/BOUNCER_SCRIMMED cases, we assume that alpha is always below threshold. 162 // This enables IMEs to control the navigation bar color. 163 // For other cases, scrim should be able to veto the light navigation bar. 164 mForceDarkForScrim = scrimState != ScrimState.BOUNCER 165 && scrimState != ScrimState.BOUNCER_SCRIMMED 166 && scrimBehindAlpha >= NAV_BAR_INVERSION_SCRIM_ALPHA_THRESHOLD 167 && !scrimInFrontColor.supportsDarkText(); 168 if (mHasLightNavigationBar && (mForceDarkForScrim != forceDarkForScrimLast)) { 169 reevaluate(); 170 } 171 } 172 isLight(int vis, int barMode, int flag)173 private boolean isLight(int vis, int barMode, int flag) { 174 boolean isTransparentBar = (barMode == MODE_TRANSPARENT 175 || barMode == MODE_LIGHTS_OUT_TRANSPARENT); 176 boolean light = (vis & flag) != 0; 177 return isTransparentBar && light; 178 } 179 animateChange()180 private boolean animateChange() { 181 if (mFingerprintUnlockController == null) { 182 return false; 183 } 184 int unlockMode = mFingerprintUnlockController.getMode(); 185 return unlockMode != FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING 186 && unlockMode != FingerprintUnlockController.MODE_WAKE_AND_UNLOCK; 187 } 188 updateStatus(Rect fullscreenStackBounds, Rect dockedStackBounds)189 private void updateStatus(Rect fullscreenStackBounds, Rect dockedStackBounds) { 190 boolean hasDockedStack = !dockedStackBounds.isEmpty(); 191 192 // If both are light or fullscreen is light and there is no docked stack, all icons get 193 // dark. 194 if ((mFullscreenLight && mDockedLight) || (mFullscreenLight && !hasDockedStack)) { 195 mStatusBarIconController.setIconsDarkArea(null); 196 mStatusBarIconController.getTransitionsController().setIconsDark(true, animateChange()); 197 198 } 199 200 // If no one is light or the fullscreen is not light and there is no docked stack, 201 // all icons become white. 202 else if ((!mFullscreenLight && !mDockedLight) || (!mFullscreenLight && !hasDockedStack)) { 203 mStatusBarIconController.getTransitionsController().setIconsDark( 204 false, animateChange()); 205 } 206 207 // Not the same for every stack, magic! 208 else { 209 Rect bounds = mFullscreenLight ? fullscreenStackBounds : dockedStackBounds; 210 if (bounds.isEmpty()) { 211 mStatusBarIconController.setIconsDarkArea(null); 212 } else { 213 mStatusBarIconController.setIconsDarkArea(bounds); 214 } 215 mStatusBarIconController.getTransitionsController().setIconsDark(true, animateChange()); 216 } 217 } 218 updateNavigation()219 private void updateNavigation() { 220 if (mNavigationBarController != null) { 221 mNavigationBarController.setIconsDark( 222 mNavigationLight, animateChange()); 223 } 224 } 225 226 @Override onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging)227 public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) { 228 229 } 230 231 @Override onPowerSaveChanged(boolean isPowerSave)232 public void onPowerSaveChanged(boolean isPowerSave) { 233 reevaluate(); 234 } 235 236 @Override dump(FileDescriptor fd, PrintWriter pw, String[] args)237 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 238 pw.println("LightBarController: "); 239 pw.print(" mSystemUiVisibility=0x"); pw.print( 240 Integer.toHexString(mSystemUiVisibility)); 241 pw.print(" mFullscreenStackVisibility=0x"); pw.print( 242 Integer.toHexString(mFullscreenStackVisibility)); 243 pw.print(" mDockedStackVisibility=0x"); pw.println( 244 Integer.toHexString(mDockedStackVisibility)); 245 246 pw.print(" mFullscreenLight="); pw.print(mFullscreenLight); 247 pw.print(" mDockedLight="); pw.println(mDockedLight); 248 249 pw.print(" mLastFullscreenBounds="); pw.print(mLastFullscreenBounds); 250 pw.print(" mLastDockedBounds="); pw.println(mLastDockedBounds); 251 252 pw.print(" mNavigationLight="); pw.print(mNavigationLight); 253 pw.print(" mHasLightNavigationBar="); pw.println(mHasLightNavigationBar); 254 255 pw.print(" mLastStatusBarMode="); pw.print(mLastStatusBarMode); 256 pw.print(" mLastNavigationBarMode="); pw.println(mLastNavigationBarMode); 257 258 pw.print(" mForceDarkForScrim="); pw.print(mForceDarkForScrim); 259 pw.print(" mQsCustomizing="); pw.println(mQsCustomizing); 260 261 pw.println(); 262 263 LightBarTransitionsController transitionsController = 264 mStatusBarIconController.getTransitionsController(); 265 if (transitionsController != null) { 266 pw.println(" StatusBarTransitionsController:"); 267 transitionsController.dump(fd, pw, args); 268 pw.println(); 269 } 270 271 if (mNavigationBarController != null) { 272 pw.println(" NavigationBarTransitionsController:"); 273 mNavigationBarController.dump(fd, pw, args); 274 pw.println(); 275 } 276 } 277 } 278