1 /* 2 * Copyright (C) 2022 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.systemui.shade; 18 19 import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE; 20 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; 21 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_BEHAVIOR_CONTROLLED; 22 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_OPTIMIZE_MEASURE; 23 24 import static com.android.systemui.statusbar.NotificationRemoteInputManager.ENABLE_REMOTE_INPUT; 25 26 import android.app.IActivityManager; 27 import android.content.Context; 28 import android.content.pm.ActivityInfo; 29 import android.content.res.Configuration; 30 import android.graphics.PixelFormat; 31 import android.graphics.Region; 32 import android.os.Binder; 33 import android.os.Build; 34 import android.os.RemoteException; 35 import android.os.Trace; 36 import android.util.Log; 37 import android.view.Display; 38 import android.view.Gravity; 39 import android.view.IWindow; 40 import android.view.IWindowSession; 41 import android.view.View; 42 import android.view.ViewGroup; 43 import android.view.WindowInsets; 44 import android.view.WindowManager; 45 import android.view.WindowManager.LayoutParams; 46 import android.view.WindowManagerGlobal; 47 48 import com.android.internal.annotations.VisibleForTesting; 49 import com.android.keyguard.KeyguardUpdateMonitor; 50 import com.android.systemui.Dumpable; 51 import com.android.systemui.R; 52 import com.android.systemui.biometrics.AuthController; 53 import com.android.systemui.colorextraction.SysuiColorExtractor; 54 import com.android.systemui.dagger.SysUISingleton; 55 import com.android.systemui.dagger.qualifiers.Background; 56 import com.android.systemui.dump.DumpManager; 57 import com.android.systemui.dump.DumpsysTableLogger; 58 import com.android.systemui.keyguard.KeyguardViewMediator; 59 import com.android.systemui.plugins.statusbar.StatusBarStateController; 60 import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener; 61 import com.android.systemui.statusbar.NotificationShadeWindowController; 62 import com.android.systemui.statusbar.StatusBarState; 63 import com.android.systemui.statusbar.SysuiStatusBarStateController; 64 import com.android.systemui.statusbar.phone.DozeParameters; 65 import com.android.systemui.statusbar.phone.KeyguardBypassController; 66 import com.android.systemui.statusbar.phone.ScreenOffAnimationController; 67 import com.android.systemui.statusbar.phone.ScrimController; 68 import com.android.systemui.statusbar.phone.StatusBarWindowCallback; 69 import com.android.systemui.statusbar.policy.ConfigurationController; 70 import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener; 71 import com.android.systemui.statusbar.policy.KeyguardStateController; 72 73 import java.io.PrintWriter; 74 import java.lang.ref.Reference; 75 import java.lang.ref.WeakReference; 76 import java.util.ArrayList; 77 import java.util.List; 78 import java.util.Objects; 79 import java.util.concurrent.Executor; 80 import java.util.function.Consumer; 81 import java.util.stream.Collectors; 82 83 import javax.inject.Inject; 84 85 /** 86 * Encapsulates all logic for the notification shade window state management. 87 */ 88 @SysUISingleton 89 public class NotificationShadeWindowControllerImpl implements NotificationShadeWindowController, 90 Dumpable, ConfigurationListener { 91 92 private static final String TAG = "NotificationShadeWindowController"; 93 private static final int MAX_STATE_CHANGES_BUFFER_SIZE = 100; 94 95 private final Context mContext; 96 private final WindowManager mWindowManager; 97 private final IActivityManager mActivityManager; 98 private final DozeParameters mDozeParameters; 99 private final KeyguardStateController mKeyguardStateController; 100 private final ShadeWindowLogger mLogger; 101 private final LayoutParams mLpChanged; 102 private final long mLockScreenDisplayTimeout; 103 private final float mKeyguardPreferredRefreshRate; // takes precedence over max 104 private final float mKeyguardMaxRefreshRate; 105 private final KeyguardViewMediator mKeyguardViewMediator; 106 private final KeyguardBypassController mKeyguardBypassController; 107 private final Executor mBackgroundExecutor; 108 private final AuthController mAuthController; 109 private ViewGroup mWindowRootView; 110 private LayoutParams mLp; 111 private boolean mHasTopUi; 112 private boolean mHasTopUiChanged; 113 private float mScreenBrightnessDoze; 114 private final NotificationShadeWindowState mCurrentState = new NotificationShadeWindowState(); 115 private OtherwisedCollapsedListener mListener; 116 private ForcePluginOpenListener mForcePluginOpenListener; 117 private Consumer<Integer> mScrimsVisibilityListener; 118 private final ArrayList<WeakReference<StatusBarWindowCallback>> 119 mCallbacks = new ArrayList<>(); 120 121 private final SysuiColorExtractor mColorExtractor; 122 private final ScreenOffAnimationController mScreenOffAnimationController; 123 /** 124 * Layout params would be aggregated and dispatched all at once if this is > 0. 125 * 126 * @see #batchApplyWindowLayoutParams(Runnable) 127 */ 128 private int mDeferWindowLayoutParams; 129 private boolean mLastKeyguardRotationAllowed; 130 131 private final NotificationShadeWindowState.Buffer mStateBuffer = 132 new NotificationShadeWindowState.Buffer(MAX_STATE_CHANGES_BUFFER_SIZE); 133 134 @Inject NotificationShadeWindowControllerImpl(Context context, WindowManager windowManager, IActivityManager activityManager, DozeParameters dozeParameters, StatusBarStateController statusBarStateController, ConfigurationController configurationController, KeyguardViewMediator keyguardViewMediator, KeyguardBypassController keyguardBypassController, @Background Executor backgroundExecutor, SysuiColorExtractor colorExtractor, DumpManager dumpManager, KeyguardStateController keyguardStateController, ScreenOffAnimationController screenOffAnimationController, AuthController authController, ShadeExpansionStateManager shadeExpansionStateManager, ShadeWindowLogger logger)135 public NotificationShadeWindowControllerImpl(Context context, WindowManager windowManager, 136 IActivityManager activityManager, DozeParameters dozeParameters, 137 StatusBarStateController statusBarStateController, 138 ConfigurationController configurationController, 139 KeyguardViewMediator keyguardViewMediator, 140 KeyguardBypassController keyguardBypassController, 141 @Background Executor backgroundExecutor, 142 SysuiColorExtractor colorExtractor, 143 DumpManager dumpManager, 144 KeyguardStateController keyguardStateController, 145 ScreenOffAnimationController screenOffAnimationController, 146 AuthController authController, 147 ShadeExpansionStateManager shadeExpansionStateManager, 148 ShadeWindowLogger logger) { 149 mContext = context; 150 mWindowManager = windowManager; 151 mActivityManager = activityManager; 152 mDozeParameters = dozeParameters; 153 mKeyguardStateController = keyguardStateController; 154 mLogger = logger; 155 mScreenBrightnessDoze = mDozeParameters.getScreenBrightnessDoze(); 156 mLpChanged = new LayoutParams(); 157 mKeyguardViewMediator = keyguardViewMediator; 158 mKeyguardBypassController = keyguardBypassController; 159 mBackgroundExecutor = backgroundExecutor; 160 mColorExtractor = colorExtractor; 161 mScreenOffAnimationController = screenOffAnimationController; 162 dumpManager.registerDumpable(getClass().getName(), this); 163 mAuthController = authController; 164 mLastKeyguardRotationAllowed = mKeyguardStateController.isKeyguardScreenRotationAllowed(); 165 mLockScreenDisplayTimeout = context.getResources() 166 .getInteger(R.integer.config_lockScreenDisplayTimeout); 167 ((SysuiStatusBarStateController) statusBarStateController) 168 .addCallback(mStateListener, 169 SysuiStatusBarStateController.RANK_STATUS_BAR_WINDOW_CONTROLLER); 170 configurationController.addCallback(this); 171 shadeExpansionStateManager.addQsExpansionListener(this::onQsExpansionChanged); 172 shadeExpansionStateManager.addFullExpansionListener(this::onShadeExpansionFullyChanged); 173 174 float desiredPreferredRefreshRate = context.getResources() 175 .getInteger(R.integer.config_keyguardRefreshRate); 176 float actualPreferredRefreshRate = -1; 177 if (desiredPreferredRefreshRate > -1) { 178 for (Display.Mode displayMode : context.getDisplay().getSupportedModes()) { 179 if (Math.abs(displayMode.getRefreshRate() - desiredPreferredRefreshRate) <= .1) { 180 actualPreferredRefreshRate = displayMode.getRefreshRate(); 181 break; 182 } 183 } 184 } 185 186 mKeyguardPreferredRefreshRate = actualPreferredRefreshRate; 187 188 // Running on the highest frame rate available can be expensive. 189 // Let's specify a preferred refresh rate, and allow higher FPS only when we 190 // know that we're not falsing (because we unlocked.) 191 mKeyguardMaxRefreshRate = context.getResources() 192 .getInteger(R.integer.config_keyguardMaxRefreshRate); 193 } 194 195 /** 196 * Register to receive notifications about status bar window state changes. 197 */ 198 @Override registerCallback(StatusBarWindowCallback callback)199 public void registerCallback(StatusBarWindowCallback callback) { 200 // Prevent adding duplicate callbacks 201 for (int i = 0; i < mCallbacks.size(); i++) { 202 if (mCallbacks.get(i).get() == callback) { 203 return; 204 } 205 } 206 mCallbacks.add(new WeakReference<>(callback)); 207 } 208 209 @Override unregisterCallback(StatusBarWindowCallback callback)210 public void unregisterCallback(StatusBarWindowCallback callback) { 211 for (int i = 0; i < mCallbacks.size(); i++) { 212 if (mCallbacks.get(i).get() == callback) { 213 mCallbacks.remove(i); 214 return; 215 } 216 } 217 } 218 219 @VisibleForTesting onShadeExpansionFullyChanged(Boolean isExpanded)220 void onShadeExpansionFullyChanged(Boolean isExpanded) { 221 if (mCurrentState.panelExpanded != isExpanded) { 222 mCurrentState.panelExpanded = isExpanded; 223 apply(mCurrentState); 224 } 225 } 226 227 /** 228 * Register a listener to monitor scrims visibility 229 * @param listener A listener to monitor scrims visibility 230 */ 231 @Override setScrimsVisibilityListener(Consumer<Integer> listener)232 public void setScrimsVisibilityListener(Consumer<Integer> listener) { 233 if (listener != null && mScrimsVisibilityListener != listener) { 234 mScrimsVisibilityListener = listener; 235 } 236 } 237 238 /** 239 * Adds the notification shade view to the window manager. 240 */ 241 @Override attach()242 public void attach() { 243 // Now that the notification shade encompasses the sliding panel and its 244 // translucent backdrop, the entire thing is made TRANSLUCENT and is 245 // hardware-accelerated. 246 mLp = new LayoutParams( 247 ViewGroup.LayoutParams.MATCH_PARENT, 248 ViewGroup.LayoutParams.MATCH_PARENT, 249 LayoutParams.TYPE_NOTIFICATION_SHADE, 250 LayoutParams.FLAG_NOT_FOCUSABLE 251 | LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING 252 | LayoutParams.FLAG_SPLIT_TOUCH 253 | LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH 254 | LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS, 255 PixelFormat.TRANSLUCENT); 256 mLp.token = new Binder(); 257 mLp.gravity = Gravity.TOP; 258 mLp.setFitInsetsTypes(0 /* types */); 259 mLp.setTitle("NotificationShade"); 260 mLp.packageName = mContext.getPackageName(); 261 mLp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; 262 mLp.privateFlags |= PRIVATE_FLAG_OPTIMIZE_MEASURE; 263 264 // We use BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE here, however, there is special logic in 265 // window manager which disables the transient show behavior. 266 // TODO: Clean this up once that behavior moves into the Shell. 267 mLp.privateFlags |= PRIVATE_FLAG_BEHAVIOR_CONTROLLED; 268 mLp.insetsFlags.behavior = BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE; 269 270 mWindowManager.addView(mWindowRootView, mLp); 271 272 mLpChanged.copyFrom(mLp); 273 onThemeChanged(); 274 275 // Make the state consistent with KeyguardViewMediator#setupLocked during initialization. 276 if (mKeyguardViewMediator.isShowingAndNotOccluded()) { 277 setKeyguardShowing(true); 278 } 279 } 280 281 @Override setWindowRootView(ViewGroup view)282 public void setWindowRootView(ViewGroup view) { 283 mWindowRootView = view; 284 } 285 286 @Override getWindowRootView()287 public ViewGroup getWindowRootView() { 288 return mWindowRootView; 289 } 290 291 @Override setDozeScreenBrightness(int value)292 public void setDozeScreenBrightness(int value) { 293 mScreenBrightnessDoze = value / 255f; 294 } 295 setKeyguardDark(boolean dark)296 private void setKeyguardDark(boolean dark) { 297 int vis = mWindowRootView.getSystemUiVisibility(); 298 if (dark) { 299 vis = vis | View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR; 300 vis = vis | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; 301 } else { 302 vis = vis & ~View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR; 303 vis = vis & ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; 304 } 305 mWindowRootView.setSystemUiVisibility(vis); 306 } 307 applyKeyguardFlags(NotificationShadeWindowState state)308 private void applyKeyguardFlags(NotificationShadeWindowState state) { 309 final boolean keyguardOrAod = state.keyguardShowing 310 || (state.dozing && mDozeParameters.getAlwaysOn()); 311 if ((keyguardOrAod && !state.mediaBackdropShowing && !state.lightRevealScrimOpaque) 312 || mKeyguardViewMediator.isAnimatingBetweenKeyguardAndSurfaceBehind()) { 313 // Show the wallpaper if we're on keyguard/AOD and the wallpaper is not occluded by a 314 // solid backdrop. Also, show it if we are currently animating between the 315 // keyguard and the surface behind the keyguard - we want to use the wallpaper as a 316 // backdrop for this animation. 317 mLpChanged.flags |= LayoutParams.FLAG_SHOW_WALLPAPER; 318 } else { 319 mLpChanged.flags &= ~LayoutParams.FLAG_SHOW_WALLPAPER; 320 } 321 322 if (state.dozing) { 323 mLpChanged.privateFlags |= LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS; 324 } else { 325 mLpChanged.privateFlags &= ~LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS; 326 } 327 328 if (mKeyguardPreferredRefreshRate > 0) { 329 boolean onKeyguard = state.statusBarState == StatusBarState.KEYGUARD 330 && !state.keyguardFadingAway && !state.keyguardGoingAway; 331 if (onKeyguard 332 && mAuthController.isUdfpsEnrolled(KeyguardUpdateMonitor.getCurrentUser())) { 333 // both max and min display refresh rate must be set to take effect: 334 mLpChanged.preferredMaxDisplayRefreshRate = mKeyguardPreferredRefreshRate; 335 mLpChanged.preferredMinDisplayRefreshRate = mKeyguardPreferredRefreshRate; 336 } else { 337 mLpChanged.preferredMaxDisplayRefreshRate = 0; 338 mLpChanged.preferredMinDisplayRefreshRate = 0; 339 } 340 Trace.setCounter("display_set_preferred_refresh_rate", 341 (long) mLpChanged.preferredMaxDisplayRefreshRate); 342 } else if (mKeyguardMaxRefreshRate > 0) { 343 boolean bypassOnKeyguard = mKeyguardBypassController.getBypassEnabled() 344 && state.statusBarState == StatusBarState.KEYGUARD 345 && !state.keyguardFadingAway && !state.keyguardGoingAway; 346 if (state.dozing || bypassOnKeyguard) { 347 mLpChanged.preferredMaxDisplayRefreshRate = mKeyguardMaxRefreshRate; 348 } else { 349 mLpChanged.preferredMaxDisplayRefreshRate = 0; 350 } 351 Trace.setCounter("display_max_refresh_rate", 352 (long) mLpChanged.preferredMaxDisplayRefreshRate); 353 } 354 355 if (state.bouncerShowing && !isDebuggable()) { 356 mLpChanged.flags |= LayoutParams.FLAG_SECURE; 357 } else { 358 mLpChanged.flags &= ~LayoutParams.FLAG_SECURE; 359 } 360 } 361 isDebuggable()362 protected boolean isDebuggable() { 363 return Build.IS_DEBUGGABLE; 364 } 365 adjustScreenOrientation(NotificationShadeWindowState state)366 private void adjustScreenOrientation(NotificationShadeWindowState state) { 367 if (state.bouncerShowing || state.isKeyguardShowingAndNotOccluded() || state.dozing) { 368 if (mKeyguardStateController.isKeyguardScreenRotationAllowed()) { 369 mLpChanged.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_USER; 370 } else { 371 mLpChanged.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_NOSENSOR; 372 } 373 } else { 374 mLpChanged.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; 375 } 376 } 377 applyFocusableFlag(NotificationShadeWindowState state)378 private void applyFocusableFlag(NotificationShadeWindowState state) { 379 boolean panelFocusable = state.notificationShadeFocusable && state.panelExpanded; 380 if (state.bouncerShowing && (state.keyguardOccluded || state.keyguardNeedsInput) 381 || ENABLE_REMOTE_INPUT && state.remoteInputActive 382 // Make the panel focusable if we're doing the screen off animation, since the light 383 // reveal scrim is drawing in the panel and should consume touch events so that they 384 // don't go to the app behind. 385 || mScreenOffAnimationController.shouldIgnoreKeyguardTouches()) { 386 mLpChanged.flags &= ~LayoutParams.FLAG_NOT_FOCUSABLE; 387 mLpChanged.flags &= ~LayoutParams.FLAG_ALT_FOCUSABLE_IM; 388 } else if (state.isKeyguardShowingAndNotOccluded() || panelFocusable) { 389 mLpChanged.flags &= ~LayoutParams.FLAG_NOT_FOCUSABLE; 390 // Make sure to remove FLAG_ALT_FOCUSABLE_IM when keyguard needs input. 391 if (state.keyguardNeedsInput && state.isKeyguardShowingAndNotOccluded()) { 392 mLpChanged.flags &= ~LayoutParams.FLAG_ALT_FOCUSABLE_IM; 393 } else { 394 mLpChanged.flags |= LayoutParams.FLAG_ALT_FOCUSABLE_IM; 395 } 396 } else { 397 mLpChanged.flags |= LayoutParams.FLAG_NOT_FOCUSABLE; 398 mLpChanged.flags &= ~LayoutParams.FLAG_ALT_FOCUSABLE_IM; 399 } 400 } 401 applyForceShowNavigationFlag(NotificationShadeWindowState state)402 private void applyForceShowNavigationFlag(NotificationShadeWindowState state) { 403 if (state.panelExpanded || state.bouncerShowing 404 || ENABLE_REMOTE_INPUT && state.remoteInputActive) { 405 mLpChanged.forciblyShownTypes |= WindowInsets.Type.navigationBars(); 406 } else { 407 mLpChanged.forciblyShownTypes &= ~WindowInsets.Type.navigationBars(); 408 } 409 } 410 applyVisibility(NotificationShadeWindowState state)411 private void applyVisibility(NotificationShadeWindowState state) { 412 boolean visible = isExpanded(state); 413 mLogger.logApplyVisibility(visible); 414 if (state.forcePluginOpen) { 415 if (mListener != null) { 416 mListener.setWouldOtherwiseCollapse(visible); 417 } 418 visible = true; 419 mLogger.d("Visibility forced to be true"); 420 } 421 if (mWindowRootView != null) { 422 if (visible) { 423 mWindowRootView.setVisibility(View.VISIBLE); 424 } else { 425 mWindowRootView.setVisibility(View.INVISIBLE); 426 } 427 } 428 } 429 isExpanded(NotificationShadeWindowState state)430 private boolean isExpanded(NotificationShadeWindowState state) { 431 boolean isExpanded = !state.forceWindowCollapsed && (state.isKeyguardShowingAndNotOccluded() 432 || state.panelVisible || state.keyguardFadingAway || state.bouncerShowing 433 || state.headsUpNotificationShowing 434 || state.scrimsVisibility != ScrimController.TRANSPARENT) 435 || state.backgroundBlurRadius > 0 436 || state.launchingActivityFromNotification; 437 mLogger.logIsExpanded(isExpanded, state.forceWindowCollapsed, 438 state.isKeyguardShowingAndNotOccluded(), state.panelVisible, 439 state.keyguardFadingAway, state.bouncerShowing, state.headsUpNotificationShowing, 440 state.scrimsVisibility != ScrimController.TRANSPARENT, 441 state.backgroundBlurRadius > 0, state.launchingActivityFromNotification); 442 return isExpanded; 443 } 444 applyFitsSystemWindows(NotificationShadeWindowState state)445 private void applyFitsSystemWindows(NotificationShadeWindowState state) { 446 boolean fitsSystemWindows = !state.isKeyguardShowingAndNotOccluded(); 447 if (mWindowRootView != null 448 && mWindowRootView.getFitsSystemWindows() != fitsSystemWindows) { 449 mWindowRootView.setFitsSystemWindows(fitsSystemWindows); 450 mWindowRootView.requestApplyInsets(); 451 } 452 } 453 applyUserActivityTimeout(NotificationShadeWindowState state)454 private void applyUserActivityTimeout(NotificationShadeWindowState state) { 455 if (state.isKeyguardShowingAndNotOccluded() 456 && state.statusBarState == StatusBarState.KEYGUARD 457 && !state.qsExpanded) { 458 mLpChanged.userActivityTimeout = state.bouncerShowing 459 ? KeyguardViewMediator.AWAKE_INTERVAL_BOUNCER_MS : mLockScreenDisplayTimeout; 460 } else { 461 mLpChanged.userActivityTimeout = -1; 462 } 463 } 464 applyInputFeatures(NotificationShadeWindowState state)465 private void applyInputFeatures(NotificationShadeWindowState state) { 466 if (state.isKeyguardShowingAndNotOccluded() 467 && state.statusBarState == StatusBarState.KEYGUARD 468 && !state.qsExpanded && !state.forceUserActivity) { 469 mLpChanged.inputFeatures |= 470 LayoutParams.INPUT_FEATURE_DISABLE_USER_ACTIVITY; 471 } else { 472 mLpChanged.inputFeatures &= 473 ~LayoutParams.INPUT_FEATURE_DISABLE_USER_ACTIVITY; 474 } 475 } 476 applyStatusBarColorSpaceAgnosticFlag(NotificationShadeWindowState state)477 private void applyStatusBarColorSpaceAgnosticFlag(NotificationShadeWindowState state) { 478 if (!isExpanded(state)) { 479 mLpChanged.privateFlags |= LayoutParams.PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC; 480 } else { 481 mLpChanged.privateFlags &= 482 ~LayoutParams.PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC; 483 } 484 } 485 applyWindowLayoutParams()486 private void applyWindowLayoutParams() { 487 if (mDeferWindowLayoutParams == 0 && mLp != null && mLp.copyFrom(mLpChanged) != 0) { 488 Trace.beginSection("updateViewLayout"); 489 mWindowManager.updateViewLayout(mWindowRootView, mLp); 490 Trace.endSection(); 491 } 492 } 493 494 @Override batchApplyWindowLayoutParams(Runnable scope)495 public void batchApplyWindowLayoutParams(Runnable scope) { 496 mDeferWindowLayoutParams++; 497 scope.run(); 498 mDeferWindowLayoutParams--; 499 applyWindowLayoutParams(); 500 } 501 apply(NotificationShadeWindowState state)502 private void apply(NotificationShadeWindowState state) { 503 logState(state); 504 applyKeyguardFlags(state); 505 applyFocusableFlag(state); 506 applyForceShowNavigationFlag(state); 507 adjustScreenOrientation(state); 508 applyVisibility(state); 509 applyUserActivityTimeout(state); 510 applyInputFeatures(state); 511 applyFitsSystemWindows(state); 512 applyModalFlag(state); 513 applyBrightness(state); 514 applyHasTopUi(state); 515 applyNotTouchable(state); 516 applyStatusBarColorSpaceAgnosticFlag(state); 517 applyWindowLayoutParams(); 518 519 if (mHasTopUi != mHasTopUiChanged) { 520 mHasTopUi = mHasTopUiChanged; 521 mBackgroundExecutor.execute(() -> { 522 try { 523 mActivityManager.setHasTopUi(mHasTopUiChanged); 524 } catch (RemoteException e) { 525 Log.e(TAG, "Failed to call setHasTopUi", e); 526 } 527 528 }); 529 } 530 notifyStateChangedCallbacks(); 531 } 532 logState(NotificationShadeWindowState state)533 private void logState(NotificationShadeWindowState state) { 534 mStateBuffer.insert( 535 state.keyguardShowing, 536 state.keyguardOccluded, 537 state.keyguardNeedsInput, 538 state.panelVisible, 539 state.panelExpanded, 540 state.notificationShadeFocusable, 541 state.bouncerShowing, 542 state.keyguardFadingAway, 543 state.keyguardGoingAway, 544 state.qsExpanded, 545 state.headsUpNotificationShowing, 546 state.lightRevealScrimOpaque, 547 state.forceWindowCollapsed, 548 state.forceDozeBrightness, 549 state.forceUserActivity, 550 state.launchingActivityFromNotification, 551 state.mediaBackdropShowing, 552 state.windowNotTouchable, 553 state.componentsForcingTopUi, 554 state.forceOpenTokens, 555 state.statusBarState, 556 state.remoteInputActive, 557 state.forcePluginOpen, 558 state.dozing, 559 state.scrimsVisibility, 560 state.backgroundBlurRadius 561 ); 562 } 563 564 @Override notifyStateChangedCallbacks()565 public void notifyStateChangedCallbacks() { 566 // Copy callbacks to separate ArrayList to avoid concurrent modification 567 List<StatusBarWindowCallback> activeCallbacks = mCallbacks.stream() 568 .map(Reference::get) 569 .filter(Objects::nonNull) 570 .collect(Collectors.toList()); 571 for (StatusBarWindowCallback cb : activeCallbacks) { 572 cb.onStateChanged(mCurrentState.keyguardShowing, 573 mCurrentState.keyguardOccluded, 574 mCurrentState.keyguardGoingAway, 575 mCurrentState.bouncerShowing, 576 mCurrentState.dozing, 577 mCurrentState.panelExpanded, 578 mCurrentState.dreaming); 579 } 580 } 581 applyModalFlag(NotificationShadeWindowState state)582 private void applyModalFlag(NotificationShadeWindowState state) { 583 if (state.headsUpNotificationShowing) { 584 mLpChanged.flags |= LayoutParams.FLAG_NOT_TOUCH_MODAL; 585 } else { 586 mLpChanged.flags &= ~LayoutParams.FLAG_NOT_TOUCH_MODAL; 587 } 588 } 589 applyBrightness(NotificationShadeWindowState state)590 private void applyBrightness(NotificationShadeWindowState state) { 591 if (state.forceDozeBrightness) { 592 mLpChanged.screenBrightness = mScreenBrightnessDoze; 593 } else { 594 mLpChanged.screenBrightness = LayoutParams.BRIGHTNESS_OVERRIDE_NONE; 595 } 596 } 597 applyHasTopUi(NotificationShadeWindowState state)598 private void applyHasTopUi(NotificationShadeWindowState state) { 599 mHasTopUiChanged = !state.componentsForcingTopUi.isEmpty() || isExpanded(state); 600 } 601 applyNotTouchable(NotificationShadeWindowState state)602 private void applyNotTouchable(NotificationShadeWindowState state) { 603 if (state.windowNotTouchable) { 604 mLpChanged.flags |= LayoutParams.FLAG_NOT_TOUCHABLE; 605 } else { 606 mLpChanged.flags &= ~LayoutParams.FLAG_NOT_TOUCHABLE; 607 } 608 } 609 610 @Override setTouchExclusionRegion(Region region)611 public void setTouchExclusionRegion(Region region) { 612 try { 613 final IWindowSession session = WindowManagerGlobal.getWindowSession(); 614 session.updateTapExcludeRegion( 615 IWindow.Stub.asInterface(getWindowRootView().getWindowToken()), 616 region); 617 } catch (RemoteException e) { 618 Log.e(TAG, "could not update the tap exclusion region:" + e); 619 } 620 } 621 622 623 @Override setKeyguardShowing(boolean showing)624 public void setKeyguardShowing(boolean showing) { 625 mCurrentState.keyguardShowing = showing; 626 apply(mCurrentState); 627 } 628 629 @Override setKeyguardOccluded(boolean occluded)630 public void setKeyguardOccluded(boolean occluded) { 631 mCurrentState.keyguardOccluded = occluded; 632 apply(mCurrentState); 633 } 634 635 @Override setKeyguardNeedsInput(boolean needsInput)636 public void setKeyguardNeedsInput(boolean needsInput) { 637 mCurrentState.keyguardNeedsInput = needsInput; 638 apply(mCurrentState); 639 } 640 641 @Override setPanelVisible(boolean visible)642 public void setPanelVisible(boolean visible) { 643 if (mCurrentState.panelVisible == visible 644 && mCurrentState.notificationShadeFocusable == visible) { 645 return; 646 } 647 mLogger.logShadeVisibleAndFocusable(visible); 648 mCurrentState.panelVisible = visible; 649 mCurrentState.notificationShadeFocusable = visible; 650 apply(mCurrentState); 651 } 652 653 @Override setNotificationShadeFocusable(boolean focusable)654 public void setNotificationShadeFocusable(boolean focusable) { 655 mLogger.logShadeFocusable(focusable); 656 mCurrentState.notificationShadeFocusable = focusable; 657 apply(mCurrentState); 658 } 659 660 @Override setBouncerShowing(boolean showing)661 public void setBouncerShowing(boolean showing) { 662 mCurrentState.bouncerShowing = showing; 663 apply(mCurrentState); 664 } 665 666 @Override setBackdropShowing(boolean showing)667 public void setBackdropShowing(boolean showing) { 668 mCurrentState.mediaBackdropShowing = showing; 669 apply(mCurrentState); 670 } 671 672 @Override setKeyguardFadingAway(boolean keyguardFadingAway)673 public void setKeyguardFadingAway(boolean keyguardFadingAway) { 674 mCurrentState.keyguardFadingAway = keyguardFadingAway; 675 apply(mCurrentState); 676 } 677 onQsExpansionChanged(Boolean expanded)678 private void onQsExpansionChanged(Boolean expanded) { 679 mCurrentState.qsExpanded = expanded; 680 apply(mCurrentState); 681 } 682 683 @Override setForceUserActivity(boolean forceUserActivity)684 public void setForceUserActivity(boolean forceUserActivity) { 685 mCurrentState.forceUserActivity = forceUserActivity; 686 apply(mCurrentState); 687 } 688 689 @Override setLaunchingActivity(boolean launching)690 public void setLaunchingActivity(boolean launching) { 691 mCurrentState.launchingActivityFromNotification = launching; 692 apply(mCurrentState); 693 } 694 695 @Override isLaunchingActivity()696 public boolean isLaunchingActivity() { 697 return mCurrentState.launchingActivityFromNotification; 698 } 699 700 @Override setScrimsVisibility(int scrimsVisibility)701 public void setScrimsVisibility(int scrimsVisibility) { 702 if (scrimsVisibility == mCurrentState.scrimsVisibility) { 703 return; 704 } 705 boolean wasExpanded = isExpanded(mCurrentState); 706 mCurrentState.scrimsVisibility = scrimsVisibility; 707 if (wasExpanded != isExpanded(mCurrentState)) { 708 apply(mCurrentState); 709 } 710 mScrimsVisibilityListener.accept(scrimsVisibility); 711 } 712 713 /** 714 * Current blur level, controller by 715 * {@link com.android.systemui.statusbar.NotificationShadeDepthController}. 716 * @param backgroundBlurRadius Radius in pixels. 717 */ 718 @Override setBackgroundBlurRadius(int backgroundBlurRadius)719 public void setBackgroundBlurRadius(int backgroundBlurRadius) { 720 if (mCurrentState.backgroundBlurRadius == backgroundBlurRadius) { 721 return; 722 } 723 mCurrentState.backgroundBlurRadius = backgroundBlurRadius; 724 apply(mCurrentState); 725 } 726 727 @Override setHeadsUpShowing(boolean showing)728 public void setHeadsUpShowing(boolean showing) { 729 mCurrentState.headsUpNotificationShowing = showing; 730 apply(mCurrentState); 731 } 732 733 @Override setLightRevealScrimOpaque(boolean opaque)734 public void setLightRevealScrimOpaque(boolean opaque) { 735 if (mCurrentState.lightRevealScrimOpaque == opaque) { 736 return; 737 } 738 mCurrentState.lightRevealScrimOpaque = opaque; 739 apply(mCurrentState); 740 } 741 742 /** 743 * @param state The {@link StatusBarStateController} of the status bar. 744 */ setStatusBarState(int state)745 private void setStatusBarState(int state) { 746 mCurrentState.statusBarState = state; 747 apply(mCurrentState); 748 } 749 750 /** 751 * Force the window to be collapsed, even if it should theoretically be expanded. 752 * Used for when a heads-up comes in but we still need to wait for the touchable regions to 753 * be computed. 754 */ 755 @Override setForceWindowCollapsed(boolean force)756 public void setForceWindowCollapsed(boolean force) { 757 mCurrentState.forceWindowCollapsed = force; 758 apply(mCurrentState); 759 } 760 761 @Override onRemoteInputActive(boolean remoteInputActive)762 public void onRemoteInputActive(boolean remoteInputActive) { 763 mCurrentState.remoteInputActive = remoteInputActive; 764 apply(mCurrentState); 765 } 766 767 /** 768 * Set whether the screen brightness is forced to the value we use for doze mode by the status 769 * bar window. 770 */ 771 @Override setForceDozeBrightness(boolean forceDozeBrightness)772 public void setForceDozeBrightness(boolean forceDozeBrightness) { 773 if (mCurrentState.forceDozeBrightness == forceDozeBrightness) { 774 return; 775 } 776 mCurrentState.forceDozeBrightness = forceDozeBrightness; 777 apply(mCurrentState); 778 } 779 780 @Override setDozing(boolean dozing)781 public void setDozing(boolean dozing) { 782 mCurrentState.dozing = dozing; 783 apply(mCurrentState); 784 } 785 786 @Override setDreaming(boolean dreaming)787 public void setDreaming(boolean dreaming) { 788 mCurrentState.dreaming = dreaming; 789 apply(mCurrentState); 790 } 791 792 @Override setForcePluginOpen(boolean forceOpen, Object token)793 public void setForcePluginOpen(boolean forceOpen, Object token) { 794 if (forceOpen) { 795 mCurrentState.forceOpenTokens.add(token); 796 } else { 797 mCurrentState.forceOpenTokens.remove(token); 798 } 799 final boolean previousForceOpenState = mCurrentState.forcePluginOpen; 800 mCurrentState.forcePluginOpen = !mCurrentState.forceOpenTokens.isEmpty(); 801 if (previousForceOpenState != mCurrentState.forcePluginOpen) { 802 apply(mCurrentState); 803 if (mForcePluginOpenListener != null) { 804 mForcePluginOpenListener.onChange(mCurrentState.forcePluginOpen); 805 } 806 } 807 } 808 809 /** 810 * The forcePluginOpen state for the status bar. 811 */ 812 @Override getForcePluginOpen()813 public boolean getForcePluginOpen() { 814 return mCurrentState.forcePluginOpen; 815 } 816 817 @Override setNotTouchable(boolean notTouchable)818 public void setNotTouchable(boolean notTouchable) { 819 mCurrentState.windowNotTouchable = notTouchable; 820 apply(mCurrentState); 821 } 822 823 /** 824 * Whether the status bar panel is expanded or not. 825 */ 826 @Override getPanelExpanded()827 public boolean getPanelExpanded() { 828 return mCurrentState.panelExpanded; 829 } 830 831 @Override setStateListener(OtherwisedCollapsedListener listener)832 public void setStateListener(OtherwisedCollapsedListener listener) { 833 mListener = listener; 834 } 835 836 @Override setForcePluginOpenListener(ForcePluginOpenListener listener)837 public void setForcePluginOpenListener(ForcePluginOpenListener listener) { 838 mForcePluginOpenListener = listener; 839 } 840 841 @Override dump(PrintWriter pw, String[] args)842 public void dump(PrintWriter pw, String[] args) { 843 pw.println(TAG + ":"); 844 pw.println(" mKeyguardMaxRefreshRate=" + mKeyguardMaxRefreshRate); 845 pw.println(" mKeyguardPreferredRefreshRate=" + mKeyguardPreferredRefreshRate); 846 pw.println(" mDeferWindowLayoutParams=" + mDeferWindowLayoutParams); 847 pw.println(mCurrentState); 848 if (mWindowRootView != null && mWindowRootView.getViewRootImpl() != null) { 849 Trace.beginSection("mWindowRootView.dump()"); 850 mWindowRootView.getViewRootImpl().dump(" ", pw); 851 Trace.endSection(); 852 } 853 Trace.beginSection("Table<State>"); 854 new DumpsysTableLogger( 855 TAG, 856 NotificationShadeWindowState.TABLE_HEADERS, 857 mStateBuffer.toList() 858 ).printTableData(pw); 859 Trace.endSection(); 860 } 861 862 @Override isShowingWallpaper()863 public boolean isShowingWallpaper() { 864 return !mCurrentState.mediaBackdropShowing; 865 } 866 867 @Override onThemeChanged()868 public void onThemeChanged() { 869 if (mWindowRootView == null) { 870 return; 871 } 872 873 final boolean useDarkText = mColorExtractor.getNeutralColors().supportsDarkText(); 874 // Make sure we have the correct navbar/statusbar colors. 875 setKeyguardDark(useDarkText); 876 } 877 878 @Override onConfigChanged(Configuration newConfig)879 public void onConfigChanged(Configuration newConfig) { 880 final boolean newScreenRotationAllowed = mKeyguardStateController 881 .isKeyguardScreenRotationAllowed(); 882 883 if (mLastKeyguardRotationAllowed != newScreenRotationAllowed) { 884 apply(mCurrentState); 885 mLastKeyguardRotationAllowed = newScreenRotationAllowed; 886 } 887 } 888 889 /** 890 * When keyguard will be dismissed but didn't start animation yet. 891 */ 892 @Override setKeyguardGoingAway(boolean goingAway)893 public void setKeyguardGoingAway(boolean goingAway) { 894 mCurrentState.keyguardGoingAway = goingAway; 895 apply(mCurrentState); 896 } 897 898 /** 899 * SystemUI may need top-ui to avoid jank when performing animations. After the 900 * animation is performed, the component should remove itself from the list of features that 901 * are forcing SystemUI to be top-ui. 902 */ 903 @Override setRequestTopUi(boolean requestTopUi, String componentTag)904 public void setRequestTopUi(boolean requestTopUi, String componentTag) { 905 if (requestTopUi) { 906 mCurrentState.componentsForcingTopUi.add(componentTag); 907 } else { 908 mCurrentState.componentsForcingTopUi.remove(componentTag); 909 } 910 apply(mCurrentState); 911 } 912 913 private final StateListener mStateListener = new StateListener() { 914 @Override 915 public void onStateChanged(int newState) { 916 setStatusBarState(newState); 917 } 918 919 @Override 920 public void onDozingChanged(boolean isDozing) { 921 setDozing(isDozing); 922 } 923 924 @Override 925 public void onDreamingChanged(boolean isDreaming) { 926 setDreaming(isDreaming); 927 } 928 }; 929 } 930