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