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