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