Home | History | Annotate | Download | only in phone
      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