1 /*
2  * Copyright (C) 2020 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.navigationbar;
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.graphics.Rect;
24 import android.util.SparseArray;
25 import android.view.View;
26 
27 import com.android.systemui.navigationbar.NavigationBarComponent.NavigationBarScope;
28 import com.android.systemui.navigationbar.buttons.ButtonDispatcher;
29 import com.android.systemui.res.R;
30 import com.android.systemui.settings.DisplayTracker;
31 import com.android.systemui.statusbar.phone.BarTransitions;
32 import com.android.systemui.statusbar.phone.LightBarTransitionsController;
33 
34 import java.io.PrintWriter;
35 import java.util.ArrayList;
36 import java.util.List;
37 
38 import javax.inject.Inject;
39 
40 /** */
41 @NavigationBarScope
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     private List<Listener> mListeners = new ArrayList<>();
48 
49     /**
50      * Notified when the color of nav bar elements changes.
51      */
52     public interface DarkIntensityListener {
53         /**
54          * Called when the color of nav bar elements changes.
55          * @param darkIntensity 0 is the lightest color, 1 is the darkest.
56          */
onDarkIntensity(float darkIntensity)57         void onDarkIntensity(float darkIntensity);
58     }
59 
60     private final NavigationBarView mView;
61     private final LightBarTransitionsController mLightTransitionsController;
62     private final DisplayTracker mDisplayTracker;
63     private final boolean mAllowAutoDimWallpaperNotVisible;
64     private boolean mWallpaperVisible;
65 
66     private boolean mLightsOut;
67     private boolean mAutoDim;
68     private View mNavButtons;
69     private int mNavBarMode = NAV_BAR_MODE_3BUTTON;
70     private List<DarkIntensityListener> mDarkIntensityListeners;
71 
72     @Inject
NavigationBarTransitions( NavigationBarView view, LightBarTransitionsController.Factory lightBarTransitionsControllerFactory, DisplayTracker displayTracker)73     public NavigationBarTransitions(
74             NavigationBarView view,
75             LightBarTransitionsController.Factory lightBarTransitionsControllerFactory,
76             DisplayTracker displayTracker) {
77         super(view, R.drawable.nav_background);
78 
79         mView = view;
80         mLightTransitionsController = lightBarTransitionsControllerFactory.create(this);
81         mDisplayTracker = displayTracker;
82         mAllowAutoDimWallpaperNotVisible = view.getContext().getResources()
83                 .getBoolean(R.bool.config_navigation_bar_enable_auto_dim_no_visible_wallpaper);
84         mDarkIntensityListeners = new ArrayList();
85 
86         mView.addOnLayoutChangeListener(
87                 (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
88                     View currentView = mView.getCurrentView();
89                     if (currentView != null) {
90                         mNavButtons = currentView.findViewById(R.id.nav_buttons);
91                         applyLightsOut(false, true);
92                     }
93                 });
94         View currentView = mView.getCurrentView();
95         if (currentView != null) {
96             mNavButtons = currentView.findViewById(R.id.nav_buttons);
97         }
98     }
99 
init()100     public void init() {
101         applyModeBackground(-1, getMode(), false /*animate*/);
102         applyLightsOut(false /*animate*/, true /*force*/);
103     }
104 
105     @Override
destroy()106     public void destroy() {
107         mLightTransitionsController.destroy();
108     }
109 
setWallpaperVisibility(boolean visible)110     void setWallpaperVisibility(boolean visible) {
111         mWallpaperVisible = visible;
112         applyLightsOut(true, false);
113     }
114 
115     @Override
setAutoDim(boolean autoDim)116     public void setAutoDim(boolean autoDim) {
117         // Ensure we aren't in gestural nav if we are triggering auto dim
118         if (autoDim && isGesturalModeOnDefaultDisplay(mView.getContext(), mDisplayTracker,
119                 mNavBarMode)) {
120             return;
121         }
122         if (mAutoDim == autoDim) return;
123         mAutoDim = autoDim;
124         applyLightsOut(true, false);
125     }
126 
setBackgroundFrame(Rect frame)127     void setBackgroundFrame(Rect frame) {
128         mBarBackground.setFrame(frame);
129     }
130 
setBackgroundOverrideAlpha(float alpha)131     void setBackgroundOverrideAlpha(float alpha) {
132         mBarBackground.setOverrideAlpha(alpha);
133     }
134 
135     @Override
isLightsOut(int mode)136     protected boolean isLightsOut(int mode) {
137         return super.isLightsOut(mode) || (mAllowAutoDimWallpaperNotVisible && mAutoDim
138                 && !mWallpaperVisible && mode != MODE_WARNING);
139     }
140 
getLightTransitionsController()141     public LightBarTransitionsController getLightTransitionsController() {
142         return mLightTransitionsController;
143     }
144 
145     @Override
onTransition(int oldMode, int newMode, boolean animate)146     protected void onTransition(int oldMode, int newMode, boolean animate) {
147         super.onTransition(oldMode, newMode, animate);
148         applyLightsOut(animate, false /*force*/);
149         for (Listener listener : mListeners) {
150             listener.onTransition(newMode);
151         }
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 
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(), mDisplayTracker, 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 
dump(PrintWriter pw)232     public void dump(PrintWriter pw) {
233         pw.println("NavigationBarTransitions:");
234         pw.println("  mMode: " + getMode());
235         pw.println("  mAlwaysOpaque: " + isAlwaysOpaque());
236         pw.println("  mAllowAutoDimWallpaperNotVisible: " + mAllowAutoDimWallpaperNotVisible);
237         pw.println("  mWallpaperVisible: " + mWallpaperVisible);
238         pw.println("  mLightsOut: " + mLightsOut);
239         pw.println("  mAutoDim: " + mAutoDim);
240         pw.println("  bg overrideAlpha: " + mBarBackground.getOverrideAlpha());
241         pw.println("  bg color: " + mBarBackground.getColor());
242         pw.println("  bg frame: " + mBarBackground.getFrame());
243     }
244 
addListener(Listener listener)245     void addListener(Listener listener) {
246         mListeners.add(listener);
247     }
248 
removeListener(Listener listener)249     void removeListener(Listener listener) {
250         mListeners.remove(listener);
251     }
252 
253     interface Listener {
onTransition(int newMode)254         void onTransition(int newMode);
255     }
256 }
257