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