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.SystemClock;
22import android.util.Slog;
23import android.view.View;
24import android.view.ViewGroup;
25import android.view.WindowManager;
26import android.view.WindowManagerPolicy.WindowState;
27
28import com.android.server.LocalServices;
29import com.android.server.statusbar.StatusBarManagerInternal;
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 mTransparentFlag;
51    private final int mStatusBarManagerId;
52    private final int mTranslucentWmFlag;
53    protected final Handler mHandler;
54    private final Object mServiceAquireLock = new Object();
55    protected StatusBarManagerInternal mStatusBarInternal;
56
57    protected WindowState mWin;
58    private int mState = StatusBarManager.WINDOW_STATE_SHOWING;
59    private int mTransientBarState;
60    private boolean mPendingShow;
61    private long mLastTranslucent;
62    private boolean mShowTransparent;
63    private boolean mSetUnHideFlagWhenNextTransparent;
64    private boolean mNoAnimationOnNextShow;
65
66    public BarController(String tag, int transientFlag, int unhideFlag, int translucentFlag,
67            int statusBarManagerId, int translucentWmFlag, int transparentFlag) {
68        mTag = "BarController." + tag;
69        mTransientFlag = transientFlag;
70        mUnhideFlag = unhideFlag;
71        mTranslucentFlag = translucentFlag;
72        mStatusBarManagerId = statusBarManagerId;
73        mTranslucentWmFlag = translucentWmFlag;
74        mTransparentFlag = transparentFlag;
75        mHandler = new Handler();
76    }
77
78    public void setWindow(WindowState win) {
79        mWin = win;
80    }
81
82    public void setShowTransparent(boolean transparent) {
83        if (transparent != mShowTransparent) {
84            mShowTransparent = transparent;
85            mSetUnHideFlagWhenNextTransparent = transparent;
86            mNoAnimationOnNextShow = true;
87        }
88    }
89
90    public void showTransient() {
91        if (mWin != null) {
92            setTransientBarState(TRANSIENT_BAR_SHOW_REQUESTED);
93        }
94    }
95
96    public boolean isTransientShowing() {
97        return mTransientBarState == TRANSIENT_BAR_SHOWING;
98    }
99
100    public boolean isTransientShowRequested() {
101        return mTransientBarState == TRANSIENT_BAR_SHOW_REQUESTED;
102    }
103
104    public boolean wasRecentlyTranslucent() {
105        return (SystemClock.uptimeMillis() - mLastTranslucent) < TRANSLUCENT_ANIMATION_DELAY_MS;
106    }
107
108    public void adjustSystemUiVisibilityLw(int oldVis, int vis) {
109        if (mWin != null && mTransientBarState == TRANSIENT_BAR_SHOWING &&
110                (vis & mTransientFlag) == 0) {
111            // sysui requests hide
112            setTransientBarState(TRANSIENT_BAR_HIDING);
113            setBarShowingLw(false);
114        } else if (mWin != null && (oldVis & mUnhideFlag) != 0 && (vis & mUnhideFlag) == 0) {
115            // sysui ready to unhide
116            setBarShowingLw(true);
117        }
118    }
119
120    public int applyTranslucentFlagLw(WindowState win, int vis, int oldVis) {
121        if (mWin != null) {
122            if (win != null && (win.getAttrs().privateFlags
123                    & WindowManager.LayoutParams.PRIVATE_FLAG_INHERIT_TRANSLUCENT_DECOR) == 0) {
124                int fl = PolicyControl.getWindowFlags(win, null);
125                if ((fl & mTranslucentWmFlag) != 0) {
126                    vis |= mTranslucentFlag;
127                } else {
128                    vis &= ~mTranslucentFlag;
129                }
130                if ((fl & WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0) {
131                    vis |= mTransparentFlag;
132                } else {
133                    vis &= ~mTransparentFlag;
134                }
135            } else {
136                vis = (vis & ~mTranslucentFlag) | (oldVis & mTranslucentFlag);
137                vis = (vis & ~mTransparentFlag) | (oldVis & mTransparentFlag);
138            }
139        }
140        return vis;
141    }
142
143    public boolean setBarShowingLw(final boolean show) {
144        if (mWin == null) return false;
145        if (show && mTransientBarState == TRANSIENT_BAR_HIDING) {
146            mPendingShow = true;
147            return false;
148        }
149        final boolean wasVis = mWin.isVisibleLw();
150        final boolean wasAnim = mWin.isAnimatingLw();
151        final boolean change = show ? mWin.showLw(!mNoAnimationOnNextShow && !skipAnimation())
152                : mWin.hideLw(!mNoAnimationOnNextShow && !skipAnimation());
153        mNoAnimationOnNextShow = false;
154        final int state = computeStateLw(wasVis, wasAnim, mWin, change);
155        final boolean stateChanged = updateStateLw(state);
156        return change || stateChanged;
157    }
158
159    protected boolean skipAnimation() {
160        return false;
161    }
162
163    private int computeStateLw(boolean wasVis, boolean wasAnim, WindowState win, boolean change) {
164        if (win.isDrawnLw()) {
165            final boolean vis = win.isVisibleLw();
166            final boolean anim = win.isAnimatingLw();
167            if (mState == StatusBarManager.WINDOW_STATE_HIDING && !change && !vis) {
168                return StatusBarManager.WINDOW_STATE_HIDDEN;
169            } else if (mState == StatusBarManager.WINDOW_STATE_HIDDEN && vis) {
170                return StatusBarManager.WINDOW_STATE_SHOWING;
171            } else if (change) {
172                if (wasVis && vis && !wasAnim && anim) {
173                    return StatusBarManager.WINDOW_STATE_HIDING;
174                } else {
175                    return StatusBarManager.WINDOW_STATE_SHOWING;
176                }
177            }
178        }
179        return mState;
180    }
181
182    private boolean updateStateLw(final int state) {
183        if (state != mState) {
184            mState = state;
185            if (DEBUG) Slog.d(mTag, "mState: " + StatusBarManager.windowStateToString(state));
186            mHandler.post(new Runnable() {
187                @Override
188                public void run() {
189                    StatusBarManagerInternal statusbar = getStatusBarInternal();
190                    if (statusbar != null) {
191                        statusbar.setWindowState(mStatusBarManagerId, state);
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 |= mTransparentFlag;
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) & mTransparentFlag) != 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 StatusBarManagerInternal getStatusBarInternal() {
278        synchronized (mServiceAquireLock) {
279            if (mStatusBarInternal == null) {
280                mStatusBarInternal = LocalServices.getService(StatusBarManagerInternal.class);
281            }
282            return mStatusBarInternal;
283        }
284    }
285
286    private static String transientBarStateToString(int state) {
287        if (state == TRANSIENT_BAR_HIDING) return "TRANSIENT_BAR_HIDING";
288        if (state == TRANSIENT_BAR_SHOWING) return "TRANSIENT_BAR_SHOWING";
289        if (state == TRANSIENT_BAR_SHOW_REQUESTED) return "TRANSIENT_BAR_SHOW_REQUESTED";
290        if (state == TRANSIENT_BAR_NONE) return "TRANSIENT_BAR_NONE";
291        throw new IllegalArgumentException("Unknown state " + state);
292    }
293
294    public void dump(PrintWriter pw, String prefix) {
295        if (mWin != null) {
296            pw.print(prefix); pw.println(mTag);
297            pw.print(prefix); pw.print("  "); pw.print("mState"); pw.print('=');
298            pw.println(StatusBarManager.windowStateToString(mState));
299            pw.print(prefix); pw.print("  "); pw.print("mTransientBar"); pw.print('=');
300            pw.println(transientBarStateToString(mTransientBarState));
301        }
302    }
303}
304