• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;
20 import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
21 
22 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT;
23 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARENT;
24 
25 import android.content.Context;
26 import android.graphics.Color;
27 import android.view.InsetsFlags;
28 import android.view.ViewDebug;
29 import android.view.WindowInsetsController.Appearance;
30 
31 import com.android.internal.colorextraction.ColorExtractor.GradientColors;
32 import com.android.internal.view.AppearanceRegion;
33 import com.android.systemui.Dumpable;
34 import com.android.systemui.R;
35 import com.android.systemui.dagger.SysUISingleton;
36 import com.android.systemui.navigationbar.NavigationModeController;
37 import com.android.systemui.plugins.DarkIconDispatcher;
38 import com.android.systemui.shared.system.QuickStepContract;
39 import com.android.systemui.statusbar.policy.BatteryController;
40 
41 import java.io.FileDescriptor;
42 import java.io.PrintWriter;
43 
44 import javax.inject.Inject;
45 
46 /**
47  * Controls how light status bar flag applies to the icons.
48  */
49 @SysUISingleton
50 public class LightBarController implements BatteryController.BatteryStateChangeCallback, Dumpable {
51 
52     private static final float NAV_BAR_INVERSION_SCRIM_ALPHA_THRESHOLD = 0.1f;
53 
54     private final SysuiDarkIconDispatcher mStatusBarIconController;
55     private final BatteryController mBatteryController;
56     private BiometricUnlockController mBiometricUnlockController;
57 
58     private LightBarTransitionsController mNavigationBarController;
59     private @Appearance int mAppearance;
60     private AppearanceRegion[] mAppearanceRegions = new AppearanceRegion[0];
61     private int mStatusBarMode;
62     private int mNavigationBarMode;
63     private int mNavigationMode;
64     private final Color mDarkModeColor;
65 
66     /**
67      * Whether the navigation bar should be light factoring in already how much alpha the scrim has
68      */
69     private boolean mNavigationLight;
70 
71     /**
72      * Whether the flags indicate that a light status bar is requested. This doesn't factor in the
73      * scrim alpha yet.
74      */
75     private boolean mHasLightNavigationBar;
76 
77     /**
78      * {@code true} if {@link #mHasLightNavigationBar} should be ignored and forcefully make
79      * {@link #mNavigationLight} {@code false}.
80      */
81     private boolean mForceDarkForScrim;
82 
83     private boolean mQsCustomizing;
84 
85     private boolean mDirectReplying;
86     private boolean mNavbarColorManagedByIme;
87 
88     @Inject
LightBarController(Context ctx, DarkIconDispatcher darkIconDispatcher, BatteryController batteryController, NavigationModeController navModeController)89     public LightBarController(Context ctx, DarkIconDispatcher darkIconDispatcher,
90             BatteryController batteryController, NavigationModeController navModeController) {
91         mDarkModeColor = Color.valueOf(ctx.getColor(R.color.dark_mode_icon_color_single_tone));
92         mStatusBarIconController = (SysuiDarkIconDispatcher) darkIconDispatcher;
93         mBatteryController = batteryController;
94         mBatteryController.addCallback(this);
95         mNavigationMode = navModeController.addListener((mode) -> {
96             mNavigationMode = mode;
97         });
98     }
99 
setNavigationBar(LightBarTransitionsController navigationBar)100     public void setNavigationBar(LightBarTransitionsController navigationBar) {
101         mNavigationBarController = navigationBar;
102         updateNavigation();
103     }
104 
setBiometricUnlockController( BiometricUnlockController biometricUnlockController)105     public void setBiometricUnlockController(
106             BiometricUnlockController biometricUnlockController) {
107         mBiometricUnlockController = biometricUnlockController;
108     }
109 
onStatusBarAppearanceChanged(AppearanceRegion[] appearanceRegions, boolean sbModeChanged, int statusBarMode, boolean navbarColorManagedByIme)110     void onStatusBarAppearanceChanged(AppearanceRegion[] appearanceRegions, boolean sbModeChanged,
111             int statusBarMode, boolean navbarColorManagedByIme) {
112         final int numStacks = appearanceRegions.length;
113         boolean stackAppearancesChanged = mAppearanceRegions.length != numStacks;
114         for (int i = 0; i < numStacks && !stackAppearancesChanged; i++) {
115             stackAppearancesChanged |= !appearanceRegions[i].equals(mAppearanceRegions[i]);
116         }
117         if (stackAppearancesChanged || sbModeChanged) {
118             mAppearanceRegions = appearanceRegions;
119             onStatusBarModeChanged(statusBarMode);
120         }
121         mNavbarColorManagedByIme = navbarColorManagedByIme;
122     }
123 
onStatusBarModeChanged(int newBarMode)124     void onStatusBarModeChanged(int newBarMode) {
125         mStatusBarMode = newBarMode;
126         updateStatus();
127     }
128 
onNavigationBarAppearanceChanged(@ppearance int appearance, boolean nbModeChanged, int navigationBarMode, boolean navbarColorManagedByIme)129     public void onNavigationBarAppearanceChanged(@Appearance int appearance, boolean nbModeChanged,
130             int navigationBarMode, boolean navbarColorManagedByIme) {
131         int diff = appearance ^ mAppearance;
132         if ((diff & APPEARANCE_LIGHT_NAVIGATION_BARS) != 0 || nbModeChanged) {
133             final boolean last = mNavigationLight;
134             mHasLightNavigationBar = isLight(appearance, navigationBarMode,
135                     APPEARANCE_LIGHT_NAVIGATION_BARS);
136             mNavigationLight = mHasLightNavigationBar
137                     && (mDirectReplying && mNavbarColorManagedByIme || !mForceDarkForScrim)
138                     && !mQsCustomizing;
139             if (mNavigationLight != last) {
140                 updateNavigation();
141             }
142         }
143         mAppearance = appearance;
144         mNavigationBarMode = navigationBarMode;
145         mNavbarColorManagedByIme = navbarColorManagedByIme;
146     }
147 
onNavigationBarModeChanged(int newBarMode)148     public void onNavigationBarModeChanged(int newBarMode) {
149         mHasLightNavigationBar = isLight(mAppearance, newBarMode, APPEARANCE_LIGHT_NAVIGATION_BARS);
150     }
151 
reevaluate()152     private void reevaluate() {
153         onStatusBarAppearanceChanged(mAppearanceRegions, true /* sbModeChange */, mStatusBarMode,
154                 mNavbarColorManagedByIme);
155         onNavigationBarAppearanceChanged(mAppearance, true /* nbModeChanged */,
156                 mNavigationBarMode, mNavbarColorManagedByIme);
157     }
158 
setQsCustomizing(boolean customizing)159     public void setQsCustomizing(boolean customizing) {
160         if (mQsCustomizing == customizing) return;
161         mQsCustomizing = customizing;
162         reevaluate();
163     }
164 
165     /**
166      * Sets whether the direct-reply is in use or not.
167      * @param directReplying {@code true} when the direct-reply is in-use.
168      */
setDirectReplying(boolean directReplying)169     public void setDirectReplying(boolean directReplying) {
170         if (mDirectReplying == directReplying) return;
171         mDirectReplying = directReplying;
172         reevaluate();
173     }
174 
setScrimState(ScrimState scrimState, float scrimBehindAlpha, GradientColors scrimInFrontColor)175     public void setScrimState(ScrimState scrimState, float scrimBehindAlpha,
176             GradientColors scrimInFrontColor) {
177         boolean forceDarkForScrimLast = mForceDarkForScrim;
178         // For BOUNCER/BOUNCER_SCRIMMED cases, we assume that alpha is always below threshold.
179         // This enables IMEs to control the navigation bar color.
180         // For other cases, scrim should be able to veto the light navigation bar.
181         mForceDarkForScrim = scrimState != ScrimState.BOUNCER
182                 && scrimState != ScrimState.BOUNCER_SCRIMMED
183                 && scrimBehindAlpha >= NAV_BAR_INVERSION_SCRIM_ALPHA_THRESHOLD
184                 && !scrimInFrontColor.supportsDarkText();
185         if (mHasLightNavigationBar && (mForceDarkForScrim != forceDarkForScrimLast)) {
186             reevaluate();
187         }
188     }
189 
isLight(int appearance, int barMode, int flag)190     private static boolean isLight(int appearance, int barMode, int flag) {
191         final boolean isTransparentBar = (barMode == MODE_TRANSPARENT
192                 || barMode == MODE_LIGHTS_OUT_TRANSPARENT);
193         final boolean light = (appearance & flag) != 0;
194         return isTransparentBar && light;
195     }
196 
animateChange()197     private boolean animateChange() {
198         if (mBiometricUnlockController == null) {
199             return false;
200         }
201         int unlockMode = mBiometricUnlockController.getMode();
202         return unlockMode != BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING
203                 && unlockMode != BiometricUnlockController.MODE_WAKE_AND_UNLOCK;
204     }
205 
updateStatus()206     private void updateStatus() {
207         final int numStacks = mAppearanceRegions.length;
208         int numLightStacks = 0;
209 
210         // We can only have maximum one light stack.
211         int indexLightStack = -1;
212 
213         for (int i = 0; i < numStacks; i++) {
214             if (isLight(mAppearanceRegions[i].getAppearance(), mStatusBarMode,
215                     APPEARANCE_LIGHT_STATUS_BARS)) {
216                 numLightStacks++;
217                 indexLightStack = i;
218             }
219         }
220 
221         // If all stacks are light, all icons get dark.
222         if (numLightStacks == numStacks) {
223             mStatusBarIconController.setIconsDarkArea(null);
224             mStatusBarIconController.getTransitionsController().setIconsDark(true, animateChange());
225 
226         }
227 
228         // If no one is light, all icons become white.
229         else if (numLightStacks == 0) {
230             mStatusBarIconController.getTransitionsController().setIconsDark(
231                     false, animateChange());
232         }
233 
234         // Not the same for every stack, magic!
235         else {
236             mStatusBarIconController.setIconsDarkArea(
237                     mAppearanceRegions[indexLightStack].getBounds());
238             mStatusBarIconController.getTransitionsController().setIconsDark(true, animateChange());
239         }
240     }
241 
updateNavigation()242     private void updateNavigation() {
243         if (mNavigationBarController != null
244                 && !QuickStepContract.isGesturalMode(mNavigationMode)) {
245             mNavigationBarController.setIconsDark(mNavigationLight, animateChange());
246         }
247     }
248 
249     @Override
onPowerSaveChanged(boolean isPowerSave)250     public void onPowerSaveChanged(boolean isPowerSave) {
251         reevaluate();
252     }
253 
254     @Override
dump(FileDescriptor fd, PrintWriter pw, String[] args)255     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
256         pw.println("LightBarController: ");
257         pw.print(" mAppearance="); pw.println(ViewDebug.flagsToString(
258                 InsetsFlags.class, "appearance", mAppearance));
259         final int numStacks = mAppearanceRegions.length;
260         for (int i = 0; i < numStacks; i++) {
261             final boolean isLight = isLight(mAppearanceRegions[i].getAppearance(), mStatusBarMode,
262                     APPEARANCE_LIGHT_STATUS_BARS);
263             pw.print(" stack #"); pw.print(i); pw.print(": ");
264             pw.print(mAppearanceRegions[i].toString()); pw.print(" isLight="); pw.println(isLight);
265         }
266 
267         pw.print(" mNavigationLight="); pw.print(mNavigationLight);
268         pw.print(" mHasLightNavigationBar="); pw.println(mHasLightNavigationBar);
269 
270         pw.print(" mStatusBarMode="); pw.print(mStatusBarMode);
271         pw.print(" mNavigationBarMode="); pw.println(mNavigationBarMode);
272 
273         pw.print(" mForceDarkForScrim="); pw.print(mForceDarkForScrim);
274         pw.print(" mQsCustomizing="); pw.print(mQsCustomizing);
275         pw.print(" mDirectReplying="); pw.println(mDirectReplying);
276         pw.print(" mNavbarColorManagedByIme="); pw.println(mNavbarColorManagedByIme);
277 
278         pw.println();
279 
280         LightBarTransitionsController transitionsController =
281                 mStatusBarIconController.getTransitionsController();
282         if (transitionsController != null) {
283             pw.println(" StatusBarTransitionsController:");
284             transitionsController.dump(fd, pw, args);
285             pw.println();
286         }
287 
288         if (mNavigationBarController != null) {
289             pw.println(" NavigationBarTransitionsController:");
290             mNavigationBarController.dump(fd, pw, args);
291             pw.println();
292         }
293     }
294 }
295