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