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.server.policy;
18 
19 import android.app.StatusBarManager;
20 import android.os.Handler;
21 import android.os.Message;
22 import android.os.SystemClock;
23 import android.util.Slog;
24 import android.view.View;
25 import android.view.ViewGroup;
26 import android.view.WindowManager;
27 import android.view.WindowManagerPolicy.WindowState;
28 
29 import com.android.server.LocalServices;
30 import com.android.server.statusbar.StatusBarManagerInternal;
31 
32 import java.io.PrintWriter;
33 
34 /**
35  * Controls state/behavior specific to a system bar window.
36  */
37 public class BarController {
38     private static final boolean DEBUG = false;
39 
40     private static final int TRANSIENT_BAR_NONE = 0;
41     private static final int TRANSIENT_BAR_SHOW_REQUESTED = 1;
42     private static final int TRANSIENT_BAR_SHOWING = 2;
43     private static final int TRANSIENT_BAR_HIDING = 3;
44 
45     private static final int TRANSLUCENT_ANIMATION_DELAY_MS = 1000;
46 
47     private static final int MSG_NAV_BAR_VISIBILITY_CHANGED = 1;
48 
49     protected final String mTag;
50     private final int mTransientFlag;
51     private final int mUnhideFlag;
52     private final int mTranslucentFlag;
53     private final int mTransparentFlag;
54     private final int mStatusBarManagerId;
55     private final int mTranslucentWmFlag;
56     protected final Handler mHandler;
57     private final Object mServiceAquireLock = new Object();
58     protected StatusBarManagerInternal mStatusBarInternal;
59 
60     protected WindowState mWin;
61     private int mState = StatusBarManager.WINDOW_STATE_SHOWING;
62     private int mTransientBarState;
63     private boolean mPendingShow;
64     private long mLastTranslucent;
65     private boolean mShowTransparent;
66     private boolean mSetUnHideFlagWhenNextTransparent;
67     private boolean mNoAnimationOnNextShow;
68 
69     private OnBarVisibilityChangedListener mVisibilityChangeListener;
70 
BarController(String tag, int transientFlag, int unhideFlag, int translucentFlag, int statusBarManagerId, int translucentWmFlag, int transparentFlag)71     public BarController(String tag, int transientFlag, int unhideFlag, int translucentFlag,
72             int statusBarManagerId, int translucentWmFlag, int transparentFlag) {
73         mTag = "BarController." + tag;
74         mTransientFlag = transientFlag;
75         mUnhideFlag = unhideFlag;
76         mTranslucentFlag = translucentFlag;
77         mStatusBarManagerId = statusBarManagerId;
78         mTranslucentWmFlag = translucentWmFlag;
79         mTransparentFlag = transparentFlag;
80         mHandler = new BarHandler();
81     }
82 
setWindow(WindowState win)83     public void setWindow(WindowState win) {
84         mWin = win;
85     }
86 
setShowTransparent(boolean transparent)87     public void setShowTransparent(boolean transparent) {
88         if (transparent != mShowTransparent) {
89             mShowTransparent = transparent;
90             mSetUnHideFlagWhenNextTransparent = transparent;
91             mNoAnimationOnNextShow = true;
92         }
93     }
94 
showTransient()95     public void showTransient() {
96         if (mWin != null) {
97             setTransientBarState(TRANSIENT_BAR_SHOW_REQUESTED);
98         }
99     }
100 
isTransientShowing()101     public boolean isTransientShowing() {
102         return mTransientBarState == TRANSIENT_BAR_SHOWING;
103     }
104 
isTransientShowRequested()105     public boolean isTransientShowRequested() {
106         return mTransientBarState == TRANSIENT_BAR_SHOW_REQUESTED;
107     }
108 
wasRecentlyTranslucent()109     public boolean wasRecentlyTranslucent() {
110         return (SystemClock.uptimeMillis() - mLastTranslucent) < TRANSLUCENT_ANIMATION_DELAY_MS;
111     }
112 
adjustSystemUiVisibilityLw(int oldVis, int vis)113     public void adjustSystemUiVisibilityLw(int oldVis, int vis) {
114         if (mWin != null && mTransientBarState == TRANSIENT_BAR_SHOWING &&
115                 (vis & mTransientFlag) == 0) {
116             // sysui requests hide
117             setTransientBarState(TRANSIENT_BAR_HIDING);
118             setBarShowingLw(false);
119         } else if (mWin != null && (oldVis & mUnhideFlag) != 0 && (vis & mUnhideFlag) == 0) {
120             // sysui ready to unhide
121             setBarShowingLw(true);
122         }
123     }
124 
applyTranslucentFlagLw(WindowState win, int vis, int oldVis)125     public int applyTranslucentFlagLw(WindowState win, int vis, int oldVis) {
126         if (mWin != null) {
127             if (win != null && (win.getAttrs().privateFlags
128                     & WindowManager.LayoutParams.PRIVATE_FLAG_INHERIT_TRANSLUCENT_DECOR) == 0) {
129                 int fl = PolicyControl.getWindowFlags(win, null);
130                 if ((fl & mTranslucentWmFlag) != 0) {
131                     vis |= mTranslucentFlag;
132                 } else {
133                     vis &= ~mTranslucentFlag;
134                 }
135                 if ((fl & WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0) {
136                     vis |= mTransparentFlag;
137                 } else {
138                     vis &= ~mTransparentFlag;
139                 }
140             } else {
141                 vis = (vis & ~mTranslucentFlag) | (oldVis & mTranslucentFlag);
142                 vis = (vis & ~mTransparentFlag) | (oldVis & mTransparentFlag);
143             }
144         }
145         return vis;
146     }
147 
setBarShowingLw(final boolean show)148     public boolean setBarShowingLw(final boolean show) {
149         if (mWin == null) return false;
150         if (show && mTransientBarState == TRANSIENT_BAR_HIDING) {
151             mPendingShow = true;
152             return false;
153         }
154         final boolean wasVis = mWin.isVisibleLw();
155         final boolean wasAnim = mWin.isAnimatingLw();
156         final boolean change = show ? mWin.showLw(!mNoAnimationOnNextShow && !skipAnimation())
157                 : mWin.hideLw(!mNoAnimationOnNextShow && !skipAnimation());
158         mNoAnimationOnNextShow = false;
159         final int state = computeStateLw(wasVis, wasAnim, mWin, change);
160         final boolean stateChanged = updateStateLw(state);
161 
162         if (change && (mVisibilityChangeListener != null)) {
163             mHandler.obtainMessage(MSG_NAV_BAR_VISIBILITY_CHANGED, show ? 1 : 0, 0).sendToTarget();
164         }
165 
166         return change || stateChanged;
167     }
168 
setOnBarVisibilityChangedListener(OnBarVisibilityChangedListener listener, boolean invokeWithState)169     void setOnBarVisibilityChangedListener(OnBarVisibilityChangedListener listener,
170             boolean invokeWithState) {
171         mVisibilityChangeListener = listener;
172         if (invokeWithState) {
173             // Optionally report the initial window state for initialization purposes
174             mHandler.obtainMessage(MSG_NAV_BAR_VISIBILITY_CHANGED,
175                     (mState == StatusBarManager.WINDOW_STATE_SHOWING) ? 1 : 0, 0).sendToTarget();
176         }
177     }
178 
skipAnimation()179     protected boolean skipAnimation() {
180         return false;
181     }
182 
computeStateLw(boolean wasVis, boolean wasAnim, WindowState win, boolean change)183     private int computeStateLw(boolean wasVis, boolean wasAnim, WindowState win, boolean change) {
184         if (win.isDrawnLw()) {
185             final boolean vis = win.isVisibleLw();
186             final boolean anim = win.isAnimatingLw();
187             if (mState == StatusBarManager.WINDOW_STATE_HIDING && !change && !vis) {
188                 return StatusBarManager.WINDOW_STATE_HIDDEN;
189             } else if (mState == StatusBarManager.WINDOW_STATE_HIDDEN && vis) {
190                 return StatusBarManager.WINDOW_STATE_SHOWING;
191             } else if (change) {
192                 if (wasVis && vis && !wasAnim && anim) {
193                     return StatusBarManager.WINDOW_STATE_HIDING;
194                 } else {
195                     return StatusBarManager.WINDOW_STATE_SHOWING;
196                 }
197             }
198         }
199         return mState;
200     }
201 
updateStateLw(final int state)202     private boolean updateStateLw(final int state) {
203         if (state != mState) {
204             mState = state;
205             if (DEBUG) Slog.d(mTag, "mState: " + StatusBarManager.windowStateToString(state));
206             mHandler.post(new Runnable() {
207                 @Override
208                 public void run() {
209                     StatusBarManagerInternal statusbar = getStatusBarInternal();
210                     if (statusbar != null) {
211                         statusbar.setWindowState(mStatusBarManagerId, state);
212                     }
213                 }
214             });
215             return true;
216         }
217         return false;
218     }
219 
checkHiddenLw()220     public boolean checkHiddenLw() {
221         if (mWin != null && mWin.isDrawnLw()) {
222             if (!mWin.isVisibleLw() && !mWin.isAnimatingLw()) {
223                 updateStateLw(StatusBarManager.WINDOW_STATE_HIDDEN);
224             }
225             if (mTransientBarState == TRANSIENT_BAR_HIDING && !mWin.isVisibleLw()) {
226                 // Finished animating out, clean up and reset style
227                 setTransientBarState(TRANSIENT_BAR_NONE);
228                 if (mPendingShow) {
229                     setBarShowingLw(true);
230                     mPendingShow = false;
231                 }
232                 return true;
233             }
234         }
235         return false;
236     }
237 
checkShowTransientBarLw()238     public boolean checkShowTransientBarLw() {
239         if (mTransientBarState == TRANSIENT_BAR_SHOWING) {
240             if (DEBUG) Slog.d(mTag, "Not showing transient bar, already shown");
241             return false;
242         } else if (mTransientBarState == TRANSIENT_BAR_SHOW_REQUESTED) {
243             if (DEBUG) Slog.d(mTag, "Not showing transient bar, already requested");
244             return false;
245         } else if (mWin == null) {
246             if (DEBUG) Slog.d(mTag, "Not showing transient bar, bar doesn't exist");
247             return false;
248         } else if (mWin.isDisplayedLw()) {
249             if (DEBUG) Slog.d(mTag, "Not showing transient bar, bar already visible");
250             return false;
251         } else {
252             return true;
253         }
254     }
255 
updateVisibilityLw(boolean transientAllowed, int oldVis, int vis)256     public int updateVisibilityLw(boolean transientAllowed, int oldVis, int vis) {
257         if (mWin == null) return vis;
258         if (isTransientShowing() || isTransientShowRequested()) { // transient bar requested
259             if (transientAllowed) {
260                 vis |= mTransientFlag;
261                 if ((oldVis & mTransientFlag) == 0) {
262                     vis |= mUnhideFlag;  // tell sysui we're ready to unhide
263                 }
264                 setTransientBarState(TRANSIENT_BAR_SHOWING);  // request accepted
265             } else {
266                 setTransientBarState(TRANSIENT_BAR_NONE);  // request denied
267             }
268         }
269         if (mShowTransparent) {
270             vis |= mTransparentFlag;
271             if (mSetUnHideFlagWhenNextTransparent) {
272                 vis |= mUnhideFlag;
273                 mSetUnHideFlagWhenNextTransparent = false;
274             }
275         }
276         if (mTransientBarState != TRANSIENT_BAR_NONE) {
277             vis |= mTransientFlag;  // ignore clear requests until transition completes
278             vis &= ~View.SYSTEM_UI_FLAG_LOW_PROFILE;  // never show transient bars in low profile
279         }
280         if ((vis & mTranslucentFlag) != 0 || (oldVis & mTranslucentFlag) != 0 ||
281                 ((vis | oldVis) & mTransparentFlag) != 0) {
282             mLastTranslucent = SystemClock.uptimeMillis();
283         }
284         return vis;
285     }
286 
setTransientBarState(int state)287     private void setTransientBarState(int state) {
288         if (mWin != null && state != mTransientBarState) {
289             if (mTransientBarState == TRANSIENT_BAR_SHOWING || state == TRANSIENT_BAR_SHOWING) {
290                 mLastTranslucent = SystemClock.uptimeMillis();
291             }
292             mTransientBarState = state;
293             if (DEBUG) Slog.d(mTag, "mTransientBarState: " + transientBarStateToString(state));
294         }
295     }
296 
getStatusBarInternal()297     protected StatusBarManagerInternal getStatusBarInternal() {
298         synchronized (mServiceAquireLock) {
299             if (mStatusBarInternal == null) {
300                 mStatusBarInternal = LocalServices.getService(StatusBarManagerInternal.class);
301             }
302             return mStatusBarInternal;
303         }
304     }
305 
transientBarStateToString(int state)306     private static String transientBarStateToString(int state) {
307         if (state == TRANSIENT_BAR_HIDING) return "TRANSIENT_BAR_HIDING";
308         if (state == TRANSIENT_BAR_SHOWING) return "TRANSIENT_BAR_SHOWING";
309         if (state == TRANSIENT_BAR_SHOW_REQUESTED) return "TRANSIENT_BAR_SHOW_REQUESTED";
310         if (state == TRANSIENT_BAR_NONE) return "TRANSIENT_BAR_NONE";
311         throw new IllegalArgumentException("Unknown state " + state);
312     }
313 
dump(PrintWriter pw, String prefix)314     public void dump(PrintWriter pw, String prefix) {
315         if (mWin != null) {
316             pw.print(prefix); pw.println(mTag);
317             pw.print(prefix); pw.print("  "); pw.print("mState"); pw.print('=');
318             pw.println(StatusBarManager.windowStateToString(mState));
319             pw.print(prefix); pw.print("  "); pw.print("mTransientBar"); pw.print('=');
320             pw.println(transientBarStateToString(mTransientBarState));
321         }
322     }
323 
324     private class BarHandler extends Handler {
325         @Override
handleMessage(Message msg)326         public void handleMessage(Message msg) {
327             switch (msg.what) {
328                 case MSG_NAV_BAR_VISIBILITY_CHANGED:
329                     final boolean visible = msg.arg1 != 0;
330                     if (mVisibilityChangeListener != null) {
331                         mVisibilityChangeListener.onBarVisibilityChanged(visible);
332                     }
333                     break;
334             }
335         }
336     }
337 
338     interface OnBarVisibilityChangedListener {
onBarVisibilityChanged(boolean visible)339         void onBarVisibilityChanged(boolean visible);
340     }
341 }
342