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