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