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 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 87 public void setNavigationBar(LightBarTransitionsController navigationBar) { 88 mNavigationBarController = navigationBar; 89 updateNavigation(); 90 } 91 92 public void setFingerprintUnlockController( 93 FingerprintUnlockController fingerprintUnlockController) { 94 mFingerprintUnlockController = fingerprintUnlockController; 95 } 96 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 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 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 152 public void setQsCustomizing(boolean customizing) { 153 if (mQsCustomizing == customizing) return; 154 mQsCustomizing = customizing; 155 reevaluate(); 156 } 157 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 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 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 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 219 private void updateNavigation() { 220 if (mNavigationBarController != null) { 221 mNavigationBarController.setIconsDark( 222 mNavigationLight, animateChange()); 223 } 224 } 225 226 @Override 227 public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) { 228 229 } 230 231 @Override 232 public void onPowerSaveChanged(boolean isPowerSave) { 233 reevaluate(); 234 } 235 236 @Override 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