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.internal.policy.impl;
18 
19 import android.app.StatusBarManager;
20 import android.os.Handler;
21 import android.os.RemoteException;
22 import android.os.ServiceManager;
23 import android.os.SystemClock;
24 import android.util.Slog;
25 import android.view.View;
26 import android.view.WindowManager;
27 import android.view.WindowManagerPolicy.WindowState;
28 
29 import com.android.internal.statusbar.IStatusBarService;
30 
31 import java.io.PrintWriter;
32 
33 /**
34  * Controls state/behavior specific to a system bar window.
35  */
36 public class BarController {
37     private static final boolean DEBUG = false;
38 
39     private static final int TRANSIENT_BAR_NONE = 0;
40     private static final int TRANSIENT_BAR_SHOW_REQUESTED = 1;
41     private static final int TRANSIENT_BAR_SHOWING = 2;
42     private static final int TRANSIENT_BAR_HIDING = 3;
43 
44     private static final int TRANSLUCENT_ANIMATION_DELAY_MS = 1000;
45 
46     private final String mTag;
47     private final int mTransientFlag;
48     private final int mUnhideFlag;
49     private final int mTranslucentFlag;
50     private final int mStatusBarManagerId;
51     private final int mTranslucentWmFlag;
52     private final Handler mHandler;
53     private final Object mServiceAquireLock = new Object();
54     private IStatusBarService mStatusBarService;
55 
56     private WindowState mWin;
57     private int mState = StatusBarManager.WINDOW_STATE_SHOWING;
58     private int mTransientBarState;
59     private boolean mPendingShow;
60     private long mLastTranslucent;
61 
BarController(String tag, int transientFlag, int unhideFlag, int translucentFlag, int statusBarManagerId, int translucentWmFlag)62     public BarController(String tag, int transientFlag, int unhideFlag, int translucentFlag,
63             int statusBarManagerId, int translucentWmFlag) {
64         mTag = "BarController." + tag;
65         mTransientFlag = transientFlag;
66         mUnhideFlag = unhideFlag;
67         mTranslucentFlag = translucentFlag;
68         mStatusBarManagerId = statusBarManagerId;
69         mTranslucentWmFlag = translucentWmFlag;
70         mHandler = new Handler();
71     }
72 
setWindow(WindowState win)73     public void setWindow(WindowState win) {
74         mWin = win;
75     }
76 
showTransient()77     public void showTransient() {
78         if (mWin != null) {
79             setTransientBarState(TRANSIENT_BAR_SHOW_REQUESTED);
80         }
81     }
82 
isTransientShowing()83     public boolean isTransientShowing() {
84         return mTransientBarState == TRANSIENT_BAR_SHOWING;
85     }
86 
isTransientShowRequested()87     public boolean isTransientShowRequested() {
88         return mTransientBarState == TRANSIENT_BAR_SHOW_REQUESTED;
89     }
90 
wasRecentlyTranslucent()91     public boolean wasRecentlyTranslucent() {
92         return (SystemClock.uptimeMillis() - mLastTranslucent) < TRANSLUCENT_ANIMATION_DELAY_MS;
93     }
94 
adjustSystemUiVisibilityLw(int oldVis, int vis)95     public void adjustSystemUiVisibilityLw(int oldVis, int vis) {
96         if (mWin != null && mTransientBarState == TRANSIENT_BAR_SHOWING &&
97                 (vis & mTransientFlag) == 0) {
98             // sysui requests hide
99             setTransientBarState(TRANSIENT_BAR_HIDING);
100             setBarShowingLw(false);
101         } else if (mWin != null && (oldVis & mUnhideFlag) != 0 && (vis & mUnhideFlag) == 0) {
102             // sysui ready to unhide
103             setBarShowingLw(true);
104         }
105     }
106 
applyTranslucentFlagLw(WindowState win, int vis, int oldVis)107     public int applyTranslucentFlagLw(WindowState win, int vis, int oldVis) {
108         if (mWin != null) {
109             if (win != null && (win.getAttrs().privateFlags
110                     & WindowManager.LayoutParams.PRIVATE_FLAG_INHERIT_TRANSLUCENT_DECOR) == 0) {
111                 int fl = PolicyControl.getWindowFlags(win, null);
112                 if ((fl & mTranslucentWmFlag) != 0) {
113                     vis |= mTranslucentFlag;
114                 } else {
115                     vis &= ~mTranslucentFlag;
116                 }
117                 if ((fl & WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0) {
118                     vis |= View.SYSTEM_UI_TRANSPARENT;
119                 } else {
120                     vis &= ~View.SYSTEM_UI_TRANSPARENT;
121                 }
122             } else {
123                 vis = (vis & ~mTranslucentFlag) | (oldVis & mTranslucentFlag);
124                 vis = (vis & ~View.SYSTEM_UI_TRANSPARENT) | (oldVis & View.SYSTEM_UI_TRANSPARENT);
125             }
126         }
127         return vis;
128     }
129 
setBarShowingLw(final boolean show)130     public boolean setBarShowingLw(final boolean show) {
131         if (mWin == null) return false;
132         if (show && mTransientBarState == TRANSIENT_BAR_HIDING) {
133             mPendingShow = true;
134             return false;
135         }
136         final boolean wasVis = mWin.isVisibleLw();
137         final boolean wasAnim = mWin.isAnimatingLw();
138         final boolean change = show ? mWin.showLw(true) : mWin.hideLw(true);
139         final int state = computeStateLw(wasVis, wasAnim, mWin, change);
140         final boolean stateChanged = updateStateLw(state);
141         return change || stateChanged;
142     }
143 
computeStateLw(boolean wasVis, boolean wasAnim, WindowState win, boolean change)144     private int computeStateLw(boolean wasVis, boolean wasAnim, WindowState win, boolean change) {
145         if (win.hasDrawnLw()) {
146             final boolean vis = win.isVisibleLw();
147             final boolean anim = win.isAnimatingLw();
148             if (mState == StatusBarManager.WINDOW_STATE_HIDING && !change && !vis) {
149                 return StatusBarManager.WINDOW_STATE_HIDDEN;
150             } else if (mState == StatusBarManager.WINDOW_STATE_HIDDEN && vis) {
151                 return StatusBarManager.WINDOW_STATE_SHOWING;
152             } else if (change) {
153                 if (wasVis && vis && !wasAnim && anim) {
154                     return StatusBarManager.WINDOW_STATE_HIDING;
155                 } else {
156                     return StatusBarManager.WINDOW_STATE_SHOWING;
157                 }
158             }
159         }
160         return mState;
161     }
162 
updateStateLw(final int state)163     private boolean updateStateLw(final int state) {
164         if (state != mState) {
165             mState = state;
166             if (DEBUG) Slog.d(mTag, "mState: " + StatusBarManager.windowStateToString(state));
167             mHandler.post(new Runnable() {
168                 @Override
169                 public void run() {
170                     try {
171                         IStatusBarService statusbar = getStatusBarService();
172                         if (statusbar != null) {
173                             statusbar.setWindowState(mStatusBarManagerId, state);
174                         }
175                     } catch (RemoteException e) {
176                         if (DEBUG) Slog.w(mTag, "Error posting window state", e);
177                         // re-acquire status bar service next time it is needed.
178                         mStatusBarService = null;
179                     }
180                 }
181             });
182             return true;
183         }
184         return false;
185     }
186 
checkHiddenLw()187     public boolean checkHiddenLw() {
188         if (mWin != null && mWin.hasDrawnLw()) {
189             if (!mWin.isVisibleLw() && !mWin.isAnimatingLw()) {
190                 updateStateLw(StatusBarManager.WINDOW_STATE_HIDDEN);
191             }
192             if (mTransientBarState == TRANSIENT_BAR_HIDING && !mWin.isVisibleLw()) {
193                 // Finished animating out, clean up and reset style
194                 setTransientBarState(TRANSIENT_BAR_NONE);
195                 if (mPendingShow) {
196                     setBarShowingLw(true);
197                     mPendingShow = false;
198                 }
199                 return true;
200             }
201         }
202         return false;
203     }
204 
checkShowTransientBarLw()205     public boolean checkShowTransientBarLw() {
206         if (mTransientBarState == TRANSIENT_BAR_SHOWING) {
207             if (DEBUG) Slog.d(mTag, "Not showing transient bar, already shown");
208             return false;
209         } else if (mTransientBarState == TRANSIENT_BAR_SHOW_REQUESTED) {
210             if (DEBUG) Slog.d(mTag, "Not showing transient bar, already requested");
211             return false;
212         } else if (mWin == null) {
213             if (DEBUG) Slog.d(mTag, "Not showing transient bar, bar doesn't exist");
214             return false;
215         } else if (mWin.isDisplayedLw()) {
216             if (DEBUG) Slog.d(mTag, "Not showing transient bar, bar already visible");
217             return false;
218         } else {
219             return true;
220         }
221     }
222 
updateVisibilityLw(boolean transientAllowed, int oldVis, int vis)223     public int updateVisibilityLw(boolean transientAllowed, int oldVis, int vis) {
224         if (mWin == null) return vis;
225         if (isTransientShowing() || isTransientShowRequested()) { // transient bar requested
226             if (transientAllowed) {
227                 vis |= mTransientFlag;
228                 if ((oldVis & mTransientFlag) == 0) {
229                     vis |= mUnhideFlag;  // tell sysui we're ready to unhide
230                 }
231                 setTransientBarState(TRANSIENT_BAR_SHOWING);  // request accepted
232             } else {
233                 setTransientBarState(TRANSIENT_BAR_NONE);  // request denied
234             }
235         }
236         if (mTransientBarState != TRANSIENT_BAR_NONE) {
237             vis |= mTransientFlag;  // ignore clear requests until transition completes
238             vis &= ~View.SYSTEM_UI_FLAG_LOW_PROFILE;  // never show transient bars in low profile
239         }
240         if ((vis & mTranslucentFlag) != 0 || (oldVis & mTranslucentFlag) != 0 ||
241                 ((vis | oldVis) & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0) {
242             mLastTranslucent = SystemClock.uptimeMillis();
243         }
244         return vis;
245     }
246 
setTransientBarState(int state)247     private void setTransientBarState(int state) {
248         if (mWin != null && state != mTransientBarState) {
249             if (mTransientBarState == TRANSIENT_BAR_SHOWING || state == TRANSIENT_BAR_SHOWING) {
250                 mLastTranslucent = SystemClock.uptimeMillis();
251             }
252             mTransientBarState = state;
253             if (DEBUG) Slog.d(mTag, "mTransientBarState: " + transientBarStateToString(state));
254         }
255     }
256 
getStatusBarService()257     private IStatusBarService getStatusBarService() {
258         synchronized (mServiceAquireLock) {
259             if (mStatusBarService == null) {
260                 mStatusBarService = IStatusBarService.Stub.asInterface(
261                         ServiceManager.getService("statusbar"));
262             }
263             return mStatusBarService;
264         }
265     }
266 
transientBarStateToString(int state)267     private static String transientBarStateToString(int state) {
268         if (state == TRANSIENT_BAR_HIDING) return "TRANSIENT_BAR_HIDING";
269         if (state == TRANSIENT_BAR_SHOWING) return "TRANSIENT_BAR_SHOWING";
270         if (state == TRANSIENT_BAR_SHOW_REQUESTED) return "TRANSIENT_BAR_SHOW_REQUESTED";
271         if (state == TRANSIENT_BAR_NONE) return "TRANSIENT_BAR_NONE";
272         throw new IllegalArgumentException("Unknown state " + state);
273     }
274 
dump(PrintWriter pw, String prefix)275     public void dump(PrintWriter pw, String prefix) {
276         if (mWin != null) {
277             pw.print(prefix); pw.println(mTag);
278             pw.print(prefix); pw.print("  "); pw.print("mState"); pw.print('=');
279             pw.println(StatusBarManager.windowStateToString(mState));
280             pw.print(prefix); pw.print("  "); pw.print("mTransientBar"); pw.print('=');
281             pw.println(transientBarStateToString(mTransientBarState));
282         }
283     }
284 }
285