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 || mState == StatusBarManager.WINDOW_STATE_HIDING 168 || !win.isLetterboxedOverlappingWith(mContentFrame); 169 } 170 setBarShowingLw(final boolean show)171 boolean setBarShowingLw(final boolean show) { 172 if (mWin == null) return false; 173 if (show && mTransientBarState == TRANSIENT_BAR_HIDING) { 174 mPendingShow = true; 175 return false; 176 } 177 final boolean wasVis = mWin.isVisibleLw(); 178 final boolean wasAnim = mWin.isAnimatingLw(); 179 final boolean skipAnim = skipAnimation(); 180 final boolean change = show ? mWin.showLw(!mNoAnimationOnNextShow && !skipAnim) 181 : mWin.hideLw(!mNoAnimationOnNextShow && !skipAnim); 182 mNoAnimationOnNextShow = false; 183 final int state = computeStateLw(wasVis, wasAnim, mWin, change); 184 final boolean stateChanged = updateStateLw(state); 185 186 if (change && (mVisibilityChangeListener != null)) { 187 mHandler.obtainMessage(MSG_NAV_BAR_VISIBILITY_CHANGED, show ? 1 : 0, 0).sendToTarget(); 188 } 189 190 return change || stateChanged; 191 } 192 setOnBarVisibilityChangedListener(OnBarVisibilityChangedListener listener, boolean invokeWithState)193 void setOnBarVisibilityChangedListener(OnBarVisibilityChangedListener listener, 194 boolean invokeWithState) { 195 mVisibilityChangeListener = listener; 196 if (invokeWithState) { 197 // Optionally report the initial window state for initialization purposes 198 mHandler.obtainMessage(MSG_NAV_BAR_VISIBILITY_CHANGED, 199 (mState == StatusBarManager.WINDOW_STATE_SHOWING) ? 1 : 0, 0).sendToTarget(); 200 } 201 } 202 skipAnimation()203 protected boolean skipAnimation() { 204 return !mWin.isDrawnLw(); 205 } 206 computeStateLw( boolean wasVis, boolean wasAnim, WindowState win, boolean change)207 private @StatusBarManager.WindowVisibleState int computeStateLw( 208 boolean wasVis, boolean wasAnim, WindowState win, boolean change) { 209 if (win.isDrawnLw()) { 210 final boolean vis = win.isVisibleLw(); 211 final boolean anim = win.isAnimatingLw(); 212 if (mState == StatusBarManager.WINDOW_STATE_HIDING && !change && !vis) { 213 return StatusBarManager.WINDOW_STATE_HIDDEN; 214 } else if (mState == StatusBarManager.WINDOW_STATE_HIDDEN && vis) { 215 return StatusBarManager.WINDOW_STATE_SHOWING; 216 } else if (change) { 217 if (wasVis && vis && !wasAnim && anim) { 218 return StatusBarManager.WINDOW_STATE_HIDING; 219 } else { 220 return StatusBarManager.WINDOW_STATE_SHOWING; 221 } 222 } 223 } 224 return mState; 225 } 226 updateStateLw(@tatusBarManager.WindowVisibleState final int state)227 private boolean updateStateLw(@StatusBarManager.WindowVisibleState final int state) { 228 if (mWin != null && state != mState) { 229 mState = state; 230 if (DEBUG) Slog.d(mTag, "mState: " + StatusBarManager.windowStateToString(state)); 231 mHandler.post(new Runnable() { 232 @Override 233 public void run() { 234 StatusBarManagerInternal statusbar = getStatusBarInternal(); 235 if (statusbar != null) { 236 statusbar.setWindowState(mDisplayId, mStatusBarManagerId, state); 237 } 238 } 239 }); 240 return true; 241 } 242 return false; 243 } 244 checkHiddenLw()245 boolean checkHiddenLw() { 246 if (mWin != null && mWin.isDrawnLw()) { 247 if (!mWin.isVisibleLw() && !mWin.isAnimatingLw()) { 248 updateStateLw(StatusBarManager.WINDOW_STATE_HIDDEN); 249 } 250 if (mTransientBarState == TRANSIENT_BAR_HIDING && !mWin.isVisibleLw()) { 251 // Finished animating out, clean up and reset style 252 setTransientBarState(TRANSIENT_BAR_NONE); 253 if (mPendingShow) { 254 setBarShowingLw(true); 255 mPendingShow = false; 256 } 257 return true; 258 } 259 } 260 return false; 261 } 262 checkShowTransientBarLw()263 boolean checkShowTransientBarLw() { 264 if (mTransientBarState == TRANSIENT_BAR_SHOWING) { 265 if (DEBUG) Slog.d(mTag, "Not showing transient bar, already shown"); 266 return false; 267 } else if (mTransientBarState == TRANSIENT_BAR_SHOW_REQUESTED) { 268 if (DEBUG) Slog.d(mTag, "Not showing transient bar, already requested"); 269 return false; 270 } else if (mWin == null) { 271 if (DEBUG) Slog.d(mTag, "Not showing transient bar, bar doesn't exist"); 272 return false; 273 } else if (mWin.isDisplayedLw()) { 274 if (DEBUG) Slog.d(mTag, "Not showing transient bar, bar already visible"); 275 return false; 276 } else { 277 return true; 278 } 279 } 280 updateVisibilityLw(boolean transientAllowed, int oldVis, int vis)281 int updateVisibilityLw(boolean transientAllowed, int oldVis, int vis) { 282 if (mWin == null) return vis; 283 if (isTransientShowing() || isTransientShowRequested()) { // transient bar requested 284 if (transientAllowed) { 285 vis |= mTransientFlag; 286 if ((oldVis & mTransientFlag) == 0) { 287 vis |= mUnhideFlag; // tell sysui we're ready to unhide 288 } 289 setTransientBarState(TRANSIENT_BAR_SHOWING); // request accepted 290 } else { 291 setTransientBarState(TRANSIENT_BAR_NONE); // request denied 292 } 293 } 294 if (mShowTransparent) { 295 vis |= mTransparentFlag; 296 if (mSetUnHideFlagWhenNextTransparent) { 297 vis |= mUnhideFlag; 298 mSetUnHideFlagWhenNextTransparent = false; 299 } 300 } 301 if (mTransientBarState != TRANSIENT_BAR_NONE) { 302 vis |= mTransientFlag; // ignore clear requests until transition completes 303 vis &= ~View.SYSTEM_UI_FLAG_LOW_PROFILE; // never show transient bars in low profile 304 } 305 if ((vis & mTranslucentFlag) != 0 || (oldVis & mTranslucentFlag) != 0 306 || ((vis | oldVis) & mTransparentFlag) != 0) { 307 mLastTranslucent = SystemClock.uptimeMillis(); 308 } 309 return vis; 310 } 311 setTransientBarState(int state)312 private void setTransientBarState(int state) { 313 if (mWin != null && state != mTransientBarState) { 314 if (mTransientBarState == TRANSIENT_BAR_SHOWING || state == TRANSIENT_BAR_SHOWING) { 315 mLastTranslucent = SystemClock.uptimeMillis(); 316 } 317 mTransientBarState = state; 318 if (DEBUG) Slog.d(mTag, "mTransientBarState: " + transientBarStateToString(state)); 319 } 320 } 321 getStatusBarInternal()322 protected StatusBarManagerInternal getStatusBarInternal() { 323 synchronized (mServiceAquireLock) { 324 if (mStatusBarInternal == null) { 325 mStatusBarInternal = LocalServices.getService(StatusBarManagerInternal.class); 326 } 327 return mStatusBarInternal; 328 } 329 } 330 transientBarStateToString(int state)331 private static String transientBarStateToString(int state) { 332 if (state == TRANSIENT_BAR_HIDING) return "TRANSIENT_BAR_HIDING"; 333 if (state == TRANSIENT_BAR_SHOWING) return "TRANSIENT_BAR_SHOWING"; 334 if (state == TRANSIENT_BAR_SHOW_REQUESTED) return "TRANSIENT_BAR_SHOW_REQUESTED"; 335 if (state == TRANSIENT_BAR_NONE) return "TRANSIENT_BAR_NONE"; 336 throw new IllegalArgumentException("Unknown state " + state); 337 } 338 writeToProto(ProtoOutputStream proto, long fieldId)339 void writeToProto(ProtoOutputStream proto, long fieldId) { 340 final long token = proto.start(fieldId); 341 proto.write(STATE, mState); 342 proto.write(TRANSIENT_STATE, mTransientBarState); 343 proto.end(token); 344 } 345 dump(PrintWriter pw, String prefix)346 void dump(PrintWriter pw, String prefix) { 347 if (mWin != null) { 348 pw.print(prefix); pw.println(mTag); 349 pw.print(prefix); pw.print(" "); pw.print("mState"); pw.print('='); 350 pw.println(StatusBarManager.windowStateToString(mState)); 351 pw.print(prefix); pw.print(" "); pw.print("mTransientBar"); pw.print('='); 352 pw.println(transientBarStateToString(mTransientBarState)); 353 pw.print(prefix); pw.print(" mContentFrame="); pw.println(mContentFrame); 354 } 355 } 356 357 private class BarHandler extends Handler { BarHandler()358 BarHandler() { 359 super(UiThread.getHandler().getLooper()); 360 } 361 362 @Override handleMessage(Message msg)363 public void handleMessage(Message msg) { 364 switch (msg.what) { 365 case MSG_NAV_BAR_VISIBILITY_CHANGED: 366 final boolean visible = msg.arg1 != 0; 367 if (mVisibilityChangeListener != null) { 368 mVisibilityChangeListener.onBarVisibilityChanged(visible); 369 } 370 break; 371 } 372 } 373 } 374 375 interface OnBarVisibilityChangedListener { onBarVisibilityChanged(boolean visible)376 void onBarVisibilityChanged(boolean visible); 377 } 378 } 379