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