1 /*
2  * Copyright (C) 2013 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.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON;
20 
21 import static com.android.systemui.util.Utils.isGesturalModeOnDefaultDisplay;
22 
23 import android.content.Context;
24 import android.graphics.Rect;
25 import android.os.Handler;
26 import android.os.RemoteException;
27 import android.os.ServiceManager;
28 import android.util.SparseArray;
29 import android.view.Display;
30 import android.view.IWallpaperVisibilityListener;
31 import android.view.IWindowManager;
32 import android.view.View;
33 
34 import com.android.internal.statusbar.IStatusBarService;
35 import com.android.systemui.Dependency;
36 import com.android.systemui.R;
37 import com.android.systemui.statusbar.CommandQueue;
38 
39 import java.util.ArrayList;
40 import java.util.List;
41 
42 public final class NavigationBarTransitions extends BarTransitions implements
43         LightBarTransitionsController.DarkIntensityApplier {
44 
45     public static final int MIN_COLOR_ADAPT_TRANSITION_TIME = 400;
46     public static final int DEFAULT_COLOR_ADAPT_TRANSITION_TIME = 1700;
47 
48     /**
49      * Notified when the color of nav bar elements changes.
50      */
51     public interface DarkIntensityListener {
52         /**
53          * Called when the color of nav bar elements changes.
54          * @param darkIntensity 0 is the lightest color, 1 is the darkest.
55          */
onDarkIntensity(float darkIntensity)56         void onDarkIntensity(float darkIntensity);
57     }
58 
59     private final NavigationBarView mView;
60     private final LightBarTransitionsController mLightTransitionsController;
61     private final boolean mAllowAutoDimWallpaperNotVisible;
62     private boolean mWallpaperVisible;
63 
64     private boolean mLightsOut;
65     private boolean mAutoDim;
66     private View mNavButtons;
67     private int mNavBarMode = NAV_BAR_MODE_3BUTTON;
68     private List<DarkIntensityListener> mDarkIntensityListeners;
69 
70     private final Handler mHandler = Handler.getMain();
71     private final IWallpaperVisibilityListener mWallpaperVisibilityListener =
72             new IWallpaperVisibilityListener.Stub() {
73         @Override
74         public void onWallpaperVisibilityChanged(boolean newVisibility,
75         int displayId) throws RemoteException {
76             mWallpaperVisible = newVisibility;
77             mHandler.post(() -> applyLightsOut(true, false));
78         }
79     };
80 
NavigationBarTransitions(NavigationBarView view, CommandQueue commandQueue)81     public NavigationBarTransitions(NavigationBarView view, CommandQueue commandQueue) {
82         super(view, R.drawable.nav_background);
83         mView = view;
84         mLightTransitionsController = new LightBarTransitionsController(
85                 view.getContext(), this, commandQueue);
86         mAllowAutoDimWallpaperNotVisible = view.getContext().getResources()
87                 .getBoolean(R.bool.config_navigation_bar_enable_auto_dim_no_visible_wallpaper);
88         mDarkIntensityListeners = new ArrayList();
89 
90         IWindowManager windowManagerService = Dependency.get(IWindowManager.class);
91         try {
92             mWallpaperVisible = windowManagerService.registerWallpaperVisibilityListener(
93                     mWallpaperVisibilityListener, Display.DEFAULT_DISPLAY);
94         } catch (RemoteException e) {
95         }
96         mView.addOnLayoutChangeListener(
97                 (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
98                     View currentView = mView.getCurrentView();
99                     if (currentView != null) {
100                         mNavButtons = currentView.findViewById(R.id.nav_buttons);
101                         applyLightsOut(false, true);
102                     }
103                 });
104         View currentView = mView.getCurrentView();
105         if (currentView != null) {
106             mNavButtons = currentView.findViewById(R.id.nav_buttons);
107         }
108     }
109 
init()110     public void init() {
111         applyModeBackground(-1, getMode(), false /*animate*/);
112         applyLightsOut(false /*animate*/, true /*force*/);
113     }
114 
115     @Override
destroy()116     public void destroy() {
117         IWindowManager windowManagerService = Dependency.get(IWindowManager.class);
118         try {
119             windowManagerService.unregisterWallpaperVisibilityListener(mWallpaperVisibilityListener,
120                     Display.DEFAULT_DISPLAY);
121         } catch (RemoteException e) {
122         }
123     }
124 
125     @Override
setAutoDim(boolean autoDim)126     public void setAutoDim(boolean autoDim) {
127         // Ensure we aren't in gestural nav if we are triggering auto dim
128         if (autoDim && isGesturalModeOnDefaultDisplay(mView.getContext(), mNavBarMode)) return;
129         if (mAutoDim == autoDim) return;
130         mAutoDim = autoDim;
131         applyLightsOut(true, false);
132     }
133 
setBackgroundFrame(Rect frame)134     void setBackgroundFrame(Rect frame) {
135         mBarBackground.setFrame(frame);
136     }
137 
138     @Override
isLightsOut(int mode)139     protected boolean isLightsOut(int mode) {
140         return super.isLightsOut(mode) || (mAllowAutoDimWallpaperNotVisible && mAutoDim
141                 && !mWallpaperVisible && mode != MODE_WARNING);
142     }
143 
getLightTransitionsController()144     public LightBarTransitionsController getLightTransitionsController() {
145         return mLightTransitionsController;
146     }
147 
148     @Override
onTransition(int oldMode, int newMode, boolean animate)149     protected void onTransition(int oldMode, int newMode, boolean animate) {
150         super.onTransition(oldMode, newMode, animate);
151         applyLightsOut(animate, false /*force*/);
152         mView.onBarTransition(newMode);
153     }
154 
applyLightsOut(boolean animate, boolean force)155     private void applyLightsOut(boolean animate, boolean force) {
156         // apply to lights out
157         applyLightsOut(isLightsOut(getMode()), animate, force);
158     }
159 
applyLightsOut(boolean lightsOut, boolean animate, boolean force)160     private void applyLightsOut(boolean lightsOut, boolean animate, boolean force) {
161         if (!force && lightsOut == mLightsOut) return;
162 
163         mLightsOut = lightsOut;
164         if (mNavButtons == null) return;
165 
166         // ok, everyone, stop it right there
167         mNavButtons.animate().cancel();
168 
169         // Bump percentage by 10% if dark.
170         float darkBump = mLightTransitionsController.getCurrentDarkIntensity() / 10;
171         final float navButtonsAlpha = lightsOut ? 0.6f + darkBump : 1f;
172 
173         if (!animate) {
174             mNavButtons.setAlpha(navButtonsAlpha);
175         } else {
176             final int duration = lightsOut ? LIGHTS_OUT_DURATION : LIGHTS_IN_DURATION;
177             mNavButtons.animate()
178                 .alpha(navButtonsAlpha)
179                 .setDuration(duration)
180                 .start();
181         }
182     }
183 
reapplyDarkIntensity()184     public void reapplyDarkIntensity() {
185         applyDarkIntensity(mLightTransitionsController.getCurrentDarkIntensity());
186     }
187 
188     @Override
applyDarkIntensity(float darkIntensity)189     public void applyDarkIntensity(float darkIntensity) {
190         SparseArray<ButtonDispatcher> buttonDispatchers = mView.getButtonDispatchers();
191         for (int i = buttonDispatchers.size() - 1; i >= 0; i--) {
192             buttonDispatchers.valueAt(i).setDarkIntensity(darkIntensity);
193         }
194         mView.getRotationButtonController().setDarkIntensity(darkIntensity);
195         for (DarkIntensityListener listener : mDarkIntensityListeners) {
196             listener.onDarkIntensity(darkIntensity);
197         }
198         if (mAutoDim) {
199             applyLightsOut(false, true);
200         }
201     }
202 
203     @Override
getTintAnimationDuration()204     public int getTintAnimationDuration() {
205         if (isGesturalModeOnDefaultDisplay(mView.getContext(), mNavBarMode)) {
206             return Math.max(DEFAULT_COLOR_ADAPT_TRANSITION_TIME, MIN_COLOR_ADAPT_TRANSITION_TIME);
207         }
208         return LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION;
209     }
210 
onNavigationModeChanged(int mode)211     public void onNavigationModeChanged(int mode) {
212         mNavBarMode = mode;
213     }
214 
215     /**
216      * Register {@code listener} to be notified when the color of nav bar elements changes.
217      *
218      * Returns the current nav bar color.
219      */
addDarkIntensityListener(DarkIntensityListener listener)220     public float addDarkIntensityListener(DarkIntensityListener listener) {
221         mDarkIntensityListeners.add(listener);
222         return mLightTransitionsController.getCurrentDarkIntensity();
223     }
224 
225     /**
226      * Remove {@code listener} from being notified when the color of nav bar elements changes.
227      */
removeDarkIntensityListener(DarkIntensityListener listener)228     public void removeDarkIntensityListener(DarkIntensityListener listener) {
229         mDarkIntensityListeners.remove(listener);
230     }
231 }
232