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