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