Light navigation bar support (1/2)

Test: Open an app that has this flag set.

Test: android.systemui.cts.LightBarTests

Bug: 29058491
Change-Id: Idaff65fdd5c59b68ae9920726c9ea50b53f96675
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
new file mode 100644
index 0000000..b5358a1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import android.graphics.Rect;
+import android.view.View;
+
+import com.android.systemui.statusbar.policy.BatteryController;
+
+import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT;
+import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARENT;
+
+/**
+ * Controls how light status bar flag applies to the icons.
+ */
+public class LightBarController implements BatteryController.BatteryStateChangeCallback {
+
+    private static final float NAV_BAR_INVERSION_SCRIM_ALPHA_THRESHOLD = 0.1f;
+
+    private final StatusBarIconController mStatusBarIconController;
+    private final BatteryController mBatteryController;
+    private FingerprintUnlockController mFingerprintUnlockController;
+    private final NavigationBarView mNavigationBarView;
+
+    private int mSystemUiVisibility;
+    private int mFullscreenStackVisibility;
+    private int mDockedStackVisibility;
+    private boolean mFullscreenLight;
+    private boolean mDockedLight;
+    private int mLastStatusBarMode;
+    private int mLastNavigationBarMode;
+    private boolean mNavigationLight;
+    private float mScrimAlpha;
+
+    private final Rect mLastFullscreenBounds = new Rect();
+    private final Rect mLastDockedBounds = new Rect();
+
+    public LightBarController(StatusBarIconController statusBarIconController,
+            NavigationBarView navigationBarView,
+            BatteryController batteryController) {
+        mStatusBarIconController = statusBarIconController;
+        mNavigationBarView = navigationBarView;
+        mBatteryController = batteryController;
+        batteryController.addCallback(this);
+    }
+
+    public void setFingerprintUnlockController(
+            FingerprintUnlockController fingerprintUnlockController) {
+        mFingerprintUnlockController = fingerprintUnlockController;
+    }
+
+    public void onSystemUiVisibilityChanged(int vis, int fullscreenStackVis, int dockedStackVis,
+            int mask, Rect fullscreenStackBounds, Rect dockedStackBounds, boolean sbModeChanged,
+            int statusBarMode, boolean nbModeChanged, int navigationBarMode) {
+        int oldFullscreen = mFullscreenStackVisibility;
+        int newFullscreen = (oldFullscreen & ~mask) | (fullscreenStackVis & mask);
+        int diffFullscreen = newFullscreen ^ oldFullscreen;
+        int oldDocked = mDockedStackVisibility;
+        int newDocked = (oldDocked & ~mask) | (dockedStackVis & mask);
+        int diffDocked = newDocked ^ oldDocked;
+        if ((diffFullscreen & View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR) != 0
+                || (diffDocked & View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR) != 0
+                || sbModeChanged
+                || !mLastFullscreenBounds.equals(fullscreenStackBounds)
+                || !mLastDockedBounds.equals(dockedStackBounds)) {
+
+            mFullscreenLight = isLight(newFullscreen, statusBarMode,
+                    View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
+            mDockedLight = isLight(newDocked, statusBarMode, View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
+            updateStatus(fullscreenStackBounds, dockedStackBounds);
+        }
+
+        int oldVis = mSystemUiVisibility;
+        int newVis = (oldVis & ~mask) | (vis & mask);
+        int diffVis = newVis ^ oldVis;
+        if ((diffVis & View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR) != 0
+                || nbModeChanged) {
+            boolean last = mNavigationLight;
+            mNavigationLight = isNavigationLight(newVis, navigationBarMode);
+            if (mNavigationLight != last) {
+                updateNavigation();
+            }
+        }
+        mFullscreenStackVisibility = newFullscreen;
+        mDockedStackVisibility = newDocked;
+        mSystemUiVisibility = newVis;
+        mLastStatusBarMode = statusBarMode;
+        mLastNavigationBarMode = navigationBarMode;
+        mLastFullscreenBounds.set(fullscreenStackBounds);
+        mLastDockedBounds.set(dockedStackBounds);
+    }
+
+    private void reevaluate() {
+        onSystemUiVisibilityChanged(mSystemUiVisibility, mFullscreenStackVisibility,
+                mDockedStackVisibility, 0 /* mask */, mLastFullscreenBounds, mLastDockedBounds,
+                true /* sbModeChange*/, mLastStatusBarMode, true /* nbModeChange*/,
+                mLastNavigationBarMode);
+    }
+
+    public void setScrimAlpha(float alpha) {
+        mScrimAlpha = alpha;
+        reevaluate();
+    }
+
+    private boolean isNavigationLight(int vis, int barMode) {
+        return isLight(vis, barMode, View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR)
+                && mScrimAlpha < NAV_BAR_INVERSION_SCRIM_ALPHA_THRESHOLD;
+    }
+
+    private boolean isLight(int vis, int barMode, int flag) {
+        boolean isTransparentBar = (barMode == MODE_TRANSPARENT
+                || barMode == MODE_LIGHTS_OUT_TRANSPARENT);
+        boolean allowLight = isTransparentBar && !mBatteryController.isPowerSave();
+        boolean light = (vis & flag) != 0;
+        return allowLight && light;
+    }
+
+    private boolean animateChange() {
+        if (mFingerprintUnlockController == null) {
+            return false;
+        }
+        int unlockMode = mFingerprintUnlockController.getMode();
+        return unlockMode != FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING
+                && unlockMode != FingerprintUnlockController.MODE_WAKE_AND_UNLOCK;
+    }
+
+    private void updateStatus(Rect fullscreenStackBounds, Rect dockedStackBounds) {
+        boolean hasDockedStack = !dockedStackBounds.isEmpty();
+
+        // If both are light or fullscreen is light and there is no docked stack, all icons get
+        // dark.
+        if ((mFullscreenLight && mDockedLight) || (mFullscreenLight && !hasDockedStack)) {
+            mStatusBarIconController.setIconsDarkArea(null);
+            mStatusBarIconController.getTransitionsController().setIconsDark(true, animateChange());
+
+        }
+
+        // If no one is light or the fullscreen is not light and there is no docked stack,
+        // all icons become white.
+        else if ((!mFullscreenLight && !mDockedLight) || (!mFullscreenLight && !hasDockedStack)) {
+            mStatusBarIconController.getTransitionsController().setIconsDark(
+                    false, animateChange());
+        }
+
+        // Not the same for every stack, magic!
+        else {
+            Rect bounds = mFullscreenLight ? fullscreenStackBounds : dockedStackBounds;
+            if (bounds.isEmpty()) {
+                mStatusBarIconController.setIconsDarkArea(null);
+            } else {
+                mStatusBarIconController.setIconsDarkArea(bounds);
+            }
+            mStatusBarIconController.getTransitionsController().setIconsDark(true, animateChange());
+        }
+    }
+
+    private void updateNavigation() {
+        if (mNavigationBarView != null) {
+            mNavigationBarView.getLightTransitionsController().setIconsDark(
+                    mNavigationLight, animateChange());
+        }
+    }
+
+    @Override
+    public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
+
+    }
+
+    @Override
+    public void onPowerSaveChanged(boolean isPowerSave) {
+        reevaluate();
+    }
+}