1 /* 2 * Copyright (C) 2019 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 com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED; 20 import static com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING; 21 import static com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN; 22 import static com.android.systemui.statusbar.StatusBarState.KEYGUARD; 23 import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow; 24 25 import android.app.StatusBarManager; 26 import android.util.Log; 27 import android.view.GestureDetector; 28 import android.view.KeyEvent; 29 import android.view.MotionEvent; 30 import android.view.View; 31 import android.view.ViewGroup; 32 33 import com.android.internal.annotations.VisibleForTesting; 34 import com.android.keyguard.AuthKeyguardMessageArea; 35 import com.android.keyguard.KeyguardUnfoldTransition; 36 import com.android.keyguard.LockIconViewController; 37 import com.android.systemui.Dumpable; 38 import com.android.systemui.animation.ActivityTransitionAnimator; 39 import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor; 40 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor; 41 import com.android.systemui.bouncer.ui.binder.BouncerViewBinder; 42 import com.android.systemui.classifier.FalsingCollector; 43 import com.android.systemui.dagger.SysUISingleton; 44 import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor; 45 import com.android.systemui.dock.DockManager; 46 import com.android.systemui.dump.DumpManager; 47 import com.android.systemui.flags.FeatureFlagsClassic; 48 import com.android.systemui.flags.Flags; 49 import com.android.systemui.keyevent.domain.interactor.SysUIKeyEventHandler; 50 import com.android.systemui.keyguard.KeyguardUnlockAnimationController; 51 import com.android.systemui.keyguard.MigrateClocksToBlueprint; 52 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; 53 import com.android.systemui.keyguard.shared.model.Edge; 54 import com.android.systemui.keyguard.shared.model.TransitionState; 55 import com.android.systemui.keyguard.shared.model.TransitionStep; 56 import com.android.systemui.res.R; 57 import com.android.systemui.scene.shared.flag.SceneContainerFlag; 58 import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor; 59 import com.android.systemui.shared.animation.DisableSubpixelTextTransitionListener; 60 import com.android.systemui.statusbar.DragDownHelper; 61 import com.android.systemui.statusbar.LockscreenShadeTransitionController; 62 import com.android.systemui.statusbar.NotificationInsetsController; 63 import com.android.systemui.statusbar.NotificationShadeDepthController; 64 import com.android.systemui.statusbar.NotificationShadeWindowController; 65 import com.android.systemui.statusbar.SysuiStatusBarStateController; 66 import com.android.systemui.statusbar.notification.domain.interactor.NotificationLaunchAnimationInteractor; 67 import com.android.systemui.statusbar.notification.stack.AmbientState; 68 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; 69 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; 70 import com.android.systemui.statusbar.phone.CentralSurfaces; 71 import com.android.systemui.statusbar.phone.DozeScrimController; 72 import com.android.systemui.statusbar.phone.DozeServiceHost; 73 import com.android.systemui.statusbar.phone.PhoneStatusBarViewController; 74 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; 75 import com.android.systemui.statusbar.window.StatusBarWindowStateController; 76 import com.android.systemui.unfold.SysUIUnfoldComponent; 77 import com.android.systemui.unfold.UnfoldTransitionProgressProvider; 78 import com.android.systemui.util.time.SystemClock; 79 80 import kotlinx.coroutines.ExperimentalCoroutinesApi; 81 82 import java.io.PrintWriter; 83 import java.util.Optional; 84 import java.util.function.Consumer; 85 86 import javax.inject.Inject; 87 88 /** 89 * Controller for {@link NotificationShadeWindowView}. 90 */ 91 @SysUISingleton 92 public class NotificationShadeWindowViewController implements Dumpable { 93 private static final String TAG = "NotifShadeWindowVC"; 94 private final FalsingCollector mFalsingCollector; 95 private final SysuiStatusBarStateController mStatusBarStateController; 96 private final NotificationShadeWindowView mView; 97 private final NotificationShadeDepthController mDepthController; 98 private final NotificationStackScrollLayoutController mNotificationStackScrollLayoutController; 99 private final LockscreenShadeTransitionController mLockscreenShadeTransitionController; 100 private final LockIconViewController mLockIconViewController; 101 private final ShadeLogger mShadeLogger; 102 private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; 103 private final StatusBarWindowStateController mStatusBarWindowStateController; 104 private final KeyguardUnlockAnimationController mKeyguardUnlockAnimationController; 105 private final AmbientState mAmbientState; 106 private final PulsingGestureListener mPulsingGestureListener; 107 private final LockscreenHostedDreamGestureListener mLockscreenHostedDreamGestureListener; 108 private final NotificationInsetsController mNotificationInsetsController; 109 private final FeatureFlagsClassic mFeatureFlagsClassic; 110 private final SysUIKeyEventHandler mSysUIKeyEventHandler; 111 private final PrimaryBouncerInteractor mPrimaryBouncerInteractor; 112 private final AlternateBouncerInteractor mAlternateBouncerInteractor; 113 private final QuickSettingsController mQuickSettingsController; 114 private final GlanceableHubContainerController 115 mGlanceableHubContainerController; 116 private GestureDetector mPulsingWakeupGestureHandler; 117 private GestureDetector mDreamingWakeupGestureHandler; 118 private View mBrightnessMirror; 119 private boolean mTouchActive; 120 private boolean mTouchCancelled; 121 private MotionEvent mDownEvent; 122 // TODO rename to mLaunchAnimationRunning 123 private boolean mExpandAnimationRunning; 124 /** 125 * When mExpandAnimationRunning is true and the touch dispatcher receives a down even after 126 * uptime exceeds this, the dispatcher will stop blocking touches for the launch animation, 127 * which has presumabely not completed due to an error. 128 */ 129 private long mLaunchAnimationTimeout; 130 private NotificationStackScrollLayout mStackScrollLayout; 131 private PhoneStatusBarViewController mStatusBarViewController; 132 private final CentralSurfaces mService; 133 private final DozeServiceHost mDozeServiceHost; 134 private final DozeScrimController mDozeScrimController; 135 private final NotificationShadeWindowController mNotificationShadeWindowController; 136 private DragDownHelper mDragDownHelper; 137 private boolean mExpandingBelowNotch; 138 private final DockManager mDockManager; 139 private final ShadeViewController mShadeViewController; 140 private final PanelExpansionInteractor mPanelExpansionInteractor; 141 private final ShadeExpansionStateManager mShadeExpansionStateManager; 142 143 /** 144 * If {@code true}, an external touch sent in {@link #handleExternalTouch(MotionEvent)} has been 145 * intercepted and all future touch events for the gesture should be processed by this view. 146 */ 147 private boolean mExternalTouchIntercepted = false; 148 private boolean mIsTrackingBarGesture = false; 149 private boolean mIsOcclusionTransitionRunning = false; 150 private DisableSubpixelTextTransitionListener mDisableSubpixelTextTransitionListener; 151 private final Consumer<TransitionStep> mLockscreenToDreamingTransition = 152 (TransitionStep step) -> { 153 mIsOcclusionTransitionRunning = 154 step.getTransitionState() == TransitionState.RUNNING; 155 }; 156 private final SystemClock mClock; 157 158 @ExperimentalCoroutinesApi 159 @Inject NotificationShadeWindowViewController( LockscreenShadeTransitionController transitionController, FalsingCollector falsingCollector, SysuiStatusBarStateController statusBarStateController, DockManager dockManager, NotificationShadeDepthController depthController, NotificationShadeWindowView notificationShadeWindowView, ShadeViewController shadeViewController, PanelExpansionInteractor panelExpansionInteractor, ShadeExpansionStateManager shadeExpansionStateManager, NotificationStackScrollLayoutController notificationStackScrollLayoutController, StatusBarKeyguardViewManager statusBarKeyguardViewManager, StatusBarWindowStateController statusBarWindowStateController, LockIconViewController lockIconViewController, CentralSurfaces centralSurfaces, DozeServiceHost dozeServiceHost, DozeScrimController dozeScrimController, NotificationShadeWindowController controller, Optional<UnfoldTransitionProgressProvider> unfoldTransitionProgressProvider, Optional<SysUIUnfoldComponent> unfoldComponent, KeyguardUnlockAnimationController keyguardUnlockAnimationController, NotificationInsetsController notificationInsetsController, AmbientState ambientState, ShadeLogger shadeLogger, DumpManager dumpManager, PulsingGestureListener pulsingGestureListener, LockscreenHostedDreamGestureListener lockscreenHostedDreamGestureListener, KeyguardTransitionInteractor keyguardTransitionInteractor, GlanceableHubContainerController glanceableHubContainerController, NotificationLaunchAnimationInteractor notificationLaunchAnimationInteractor, FeatureFlagsClassic featureFlagsClassic, SystemClock clock, SysUIKeyEventHandler sysUIKeyEventHandler, QuickSettingsController quickSettingsController, PrimaryBouncerInteractor primaryBouncerInteractor, AlternateBouncerInteractor alternateBouncerInteractor, BouncerViewBinder bouncerViewBinder)160 public NotificationShadeWindowViewController( 161 LockscreenShadeTransitionController transitionController, 162 FalsingCollector falsingCollector, 163 SysuiStatusBarStateController statusBarStateController, 164 DockManager dockManager, 165 NotificationShadeDepthController depthController, 166 NotificationShadeWindowView notificationShadeWindowView, 167 ShadeViewController shadeViewController, 168 PanelExpansionInteractor panelExpansionInteractor, 169 ShadeExpansionStateManager shadeExpansionStateManager, 170 NotificationStackScrollLayoutController notificationStackScrollLayoutController, 171 StatusBarKeyguardViewManager statusBarKeyguardViewManager, 172 StatusBarWindowStateController statusBarWindowStateController, 173 LockIconViewController lockIconViewController, 174 CentralSurfaces centralSurfaces, 175 DozeServiceHost dozeServiceHost, 176 DozeScrimController dozeScrimController, 177 NotificationShadeWindowController controller, 178 Optional<UnfoldTransitionProgressProvider> unfoldTransitionProgressProvider, 179 Optional<SysUIUnfoldComponent> unfoldComponent, 180 KeyguardUnlockAnimationController keyguardUnlockAnimationController, 181 NotificationInsetsController notificationInsetsController, 182 AmbientState ambientState, 183 ShadeLogger shadeLogger, 184 DumpManager dumpManager, 185 PulsingGestureListener pulsingGestureListener, 186 LockscreenHostedDreamGestureListener lockscreenHostedDreamGestureListener, 187 KeyguardTransitionInteractor keyguardTransitionInteractor, 188 GlanceableHubContainerController glanceableHubContainerController, 189 NotificationLaunchAnimationInteractor notificationLaunchAnimationInteractor, 190 FeatureFlagsClassic featureFlagsClassic, 191 SystemClock clock, 192 SysUIKeyEventHandler sysUIKeyEventHandler, 193 QuickSettingsController quickSettingsController, 194 PrimaryBouncerInteractor primaryBouncerInteractor, 195 AlternateBouncerInteractor alternateBouncerInteractor, 196 BouncerViewBinder bouncerViewBinder) { 197 mLockscreenShadeTransitionController = transitionController; 198 mFalsingCollector = falsingCollector; 199 mStatusBarStateController = statusBarStateController; 200 mView = notificationShadeWindowView; 201 mDockManager = dockManager; 202 mShadeViewController = shadeViewController; 203 mPanelExpansionInteractor = panelExpansionInteractor; 204 mShadeExpansionStateManager = shadeExpansionStateManager; 205 mDepthController = depthController; 206 mNotificationStackScrollLayoutController = notificationStackScrollLayoutController; 207 mStatusBarKeyguardViewManager = statusBarKeyguardViewManager; 208 mStatusBarWindowStateController = statusBarWindowStateController; 209 mLockIconViewController = lockIconViewController; 210 mShadeLogger = shadeLogger; 211 mService = centralSurfaces; 212 mDozeServiceHost = dozeServiceHost; 213 mDozeScrimController = dozeScrimController; 214 mNotificationShadeWindowController = controller; 215 mKeyguardUnlockAnimationController = keyguardUnlockAnimationController; 216 mAmbientState = ambientState; 217 mPulsingGestureListener = pulsingGestureListener; 218 mLockscreenHostedDreamGestureListener = lockscreenHostedDreamGestureListener; 219 mNotificationInsetsController = notificationInsetsController; 220 mGlanceableHubContainerController = glanceableHubContainerController; 221 mFeatureFlagsClassic = featureFlagsClassic; 222 mSysUIKeyEventHandler = sysUIKeyEventHandler; 223 mPrimaryBouncerInteractor = primaryBouncerInteractor; 224 mAlternateBouncerInteractor = alternateBouncerInteractor; 225 mQuickSettingsController = quickSettingsController; 226 227 // This view is not part of the newly inflated expanded status bar. 228 mBrightnessMirror = mView.findViewById(R.id.brightness_mirror_container); 229 mDisableSubpixelTextTransitionListener = new DisableSubpixelTextTransitionListener(mView); 230 bouncerViewBinder.bind(mView.findViewById(R.id.keyguard_bouncer_container)); 231 232 collectFlow(mView, keyguardTransitionInteractor.transition( 233 Edge.create(LOCKSCREEN, DREAMING)), 234 mLockscreenToDreamingTransition); 235 collectFlow( 236 mView, 237 notificationLaunchAnimationInteractor.isLaunchAnimationRunning(), 238 this::setExpandAnimationRunning); 239 240 var keyguardUnfoldTransition = unfoldComponent.map( 241 SysUIUnfoldComponent::getKeyguardUnfoldTransition); 242 var notificationPanelUnfoldAnimationController = unfoldComponent.map( 243 SysUIUnfoldComponent::getNotificationPanelUnfoldAnimationController); 244 245 keyguardUnfoldTransition.ifPresent(KeyguardUnfoldTransition::setup); 246 notificationPanelUnfoldAnimationController.ifPresent(u -> u.setup(mView)); 247 248 mClock = clock; 249 if (featureFlagsClassic.isEnabled(Flags.SPLIT_SHADE_SUBPIXEL_OPTIMIZATION)) { 250 unfoldTransitionProgressProvider.ifPresent( 251 progressProvider -> progressProvider.addCallback( 252 mDisableSubpixelTextTransitionListener)); 253 } 254 255 lockIconViewController.setLockIconView(mView.findViewById(R.id.lock_icon_view)); 256 dumpManager.registerDumpable(this); 257 } 258 259 /** 260 * @return Location where to place the KeyguardMessageArea 261 */ getKeyguardMessageArea()262 public AuthKeyguardMessageArea getKeyguardMessageArea() { 263 return mView.findViewById(R.id.keyguard_message_area); 264 } 265 logDownDispatch(MotionEvent ev, String msg, Boolean result)266 private Boolean logDownDispatch(MotionEvent ev, String msg, Boolean result) { 267 if (ev.getAction() == MotionEvent.ACTION_DOWN) { 268 mShadeLogger.logShadeWindowDispatch(ev, msg, result); 269 } 270 return result; 271 } 272 273 /** 274 * Handle a touch event while dreaming or on the hub by forwarding the event to the content 275 * view. 276 * <p> 277 * Since important logic for handling touches lives in the dispatch/intercept phases, we 278 * simulate going through all of these stages before sending onTouchEvent if intercepted. 279 * 280 * @param event The event to forward. 281 */ handleExternalTouch(MotionEvent event)282 public void handleExternalTouch(MotionEvent event) { 283 if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { 284 mExternalTouchIntercepted = false; 285 } 286 287 if (!mView.dispatchTouchEvent(event)) { 288 return; 289 } 290 if (!mExternalTouchIntercepted) { 291 mExternalTouchIntercepted = mView.onInterceptTouchEvent(event); 292 } 293 if (mExternalTouchIntercepted) { 294 mView.onTouchEvent(event); 295 } 296 } 297 298 /** Inflates the {@link R.layout#status_bar_expanded} layout and sets it up. */ setupExpandedStatusBar()299 public void setupExpandedStatusBar() { 300 mStackScrollLayout = mView.findViewById(R.id.notification_stack_scroller); 301 mPulsingWakeupGestureHandler = new GestureDetector(mView.getContext(), 302 mPulsingGestureListener); 303 if (mFeatureFlagsClassic.isEnabled(LOCKSCREEN_WALLPAPER_DREAM_ENABLED)) { 304 mDreamingWakeupGestureHandler = new GestureDetector(mView.getContext(), 305 mLockscreenHostedDreamGestureListener); 306 } 307 mView.setLayoutInsetsController(mNotificationInsetsController); 308 mView.setInteractionEventHandler(new NotificationShadeWindowView.InteractionEventHandler() { 309 boolean mUseDragDownHelperForTouch = false; 310 boolean mLastInterceptWasDragDownHelper = false; 311 312 @Override 313 public Boolean handleDispatchTouchEvent(MotionEvent ev) { 314 if (mStatusBarViewController == null) { // Fix for b/192490822 315 return logDownDispatch(ev, 316 "Ignoring touch while statusBarView not yet set", false); 317 } 318 boolean isDown = ev.getActionMasked() == MotionEvent.ACTION_DOWN; 319 boolean isUp = ev.getActionMasked() == MotionEvent.ACTION_UP; 320 boolean isCancel = ev.getActionMasked() == MotionEvent.ACTION_CANCEL; 321 322 boolean expandingBelowNotch = mExpandingBelowNotch; 323 if (isUp || isCancel) { 324 mExpandingBelowNotch = false; 325 } 326 327 // Reset manual touch dispatch state here but make sure the UP/CANCEL event still 328 // gets delivered. 329 if (!isCancel && mService.shouldIgnoreTouch()) { 330 return logDownDispatch(ev, "touch ignored by CS", false); 331 } 332 333 if (isDown) { 334 mTouchActive = true; 335 mTouchCancelled = false; 336 mDownEvent = ev; 337 if (MigrateClocksToBlueprint.isEnabled()) { 338 mService.userActivity(); 339 } 340 } else if (ev.getActionMasked() == MotionEvent.ACTION_UP 341 || ev.getActionMasked() == MotionEvent.ACTION_CANCEL) { 342 mTouchActive = false; 343 mDownEvent = null; 344 } 345 if (mTouchCancelled) { 346 return logDownDispatch(ev, "touch cancelled", false); 347 } 348 if (mExpandAnimationRunning) { 349 if (isDown && mClock.uptimeMillis() > mLaunchAnimationTimeout) { 350 Log.wtf(TAG, "NSWVC: launch animation timed out"); 351 setExpandAnimationRunning(false); 352 } else { 353 return logDownDispatch(ev, "expand animation running", false); 354 } 355 } 356 357 if (mKeyguardUnlockAnimationController.isPlayingCannedUnlockAnimation()) { 358 // If the user was sliding their finger across the lock screen, 359 // we may have been intercepting the touch and forwarding it to the 360 // UDFPS affordance via mStatusBarKeyguardViewManager.onTouch (see below). 361 // If this touch ended up unlocking the device, we want to cancel the touch 362 // immediately, so we don't cause swipe or expand animations afterwards. 363 cancelCurrentTouch(); 364 return true; 365 } 366 367 if (mIsOcclusionTransitionRunning) { 368 return logDownDispatch(ev, "occlusion transition running", false); 369 } 370 371 mFalsingCollector.onTouchEvent(ev); 372 mPulsingWakeupGestureHandler.onTouchEvent(ev); 373 374 if (!SceneContainerFlag.isEnabled() 375 && mGlanceableHubContainerController.onTouchEvent(ev)) { 376 // GlanceableHubContainerController is only used pre-flexiglass. 377 return logDownDispatch(ev, "dispatched to glanceable hub container", true); 378 } 379 if (mDreamingWakeupGestureHandler != null 380 && mDreamingWakeupGestureHandler.onTouchEvent(ev)) { 381 return logDownDispatch(ev, "dream wakeup gesture handled", true); 382 } 383 if (mStatusBarKeyguardViewManager.dispatchTouchEvent(ev)) { 384 return logDownDispatch(ev, "dispatched to Keyguard", true); 385 } 386 if (mBrightnessMirror != null 387 && mBrightnessMirror.getVisibility() == View.VISIBLE) { 388 // Disallow new pointers while the brightness mirror is visible. This is so that 389 // you can't touch anything other than the brightness slider while the mirror is 390 // showing and the rest of the panel is transparent. 391 if (ev.getActionMasked() == MotionEvent.ACTION_POINTER_DOWN) { 392 return logDownDispatch(ev, "disallowed new pointer", false); 393 } 394 } 395 if (isDown) { 396 mNotificationStackScrollLayoutController.closeControlsIfOutsideTouch(ev); 397 } 398 399 if (mStatusBarStateController.isDozing()) { 400 mDozeScrimController.extendPulse(); 401 } 402 403 // In case we start outside of the view bounds (below the status bar), we need to 404 // dispatch the touch manually as the view system can't accommodate for touches 405 // outside of the regular view bounds. 406 if (isDown && ev.getY() >= mView.getBottom()) { 407 mExpandingBelowNotch = true; 408 expandingBelowNotch = true; 409 } 410 if (expandingBelowNotch) { 411 return logDownDispatch(ev, 412 "expand below notch. sending touch to status bar", 413 mStatusBarViewController.sendTouchToView(ev)); 414 } 415 416 if (!mIsTrackingBarGesture && isDown 417 && mPanelExpansionInteractor.isFullyCollapsed()) { 418 float x = ev.getRawX(); 419 float y = ev.getRawY(); 420 if (mStatusBarViewController.touchIsWithinView(x, y)) { 421 if (!(MigrateClocksToBlueprint.isEnabled() 422 && mPrimaryBouncerInteractor.isBouncerShowing())) { 423 if (mStatusBarWindowStateController.windowIsShowing()) { 424 mIsTrackingBarGesture = true; 425 return logDownDispatch(ev, "sending touch to status bar", 426 mStatusBarViewController.sendTouchToView(ev)); 427 } else { 428 return logDownDispatch(ev, "hidden or hiding", true); 429 } 430 } 431 } 432 } else if (mIsTrackingBarGesture) { 433 final boolean sendToStatusBar = mStatusBarViewController.sendTouchToView(ev); 434 if (isUp || isCancel) { 435 mIsTrackingBarGesture = false; 436 } 437 return logDownDispatch(ev, "sending bar gesture to status bar", 438 sendToStatusBar); 439 } 440 return logDownDispatch(ev, "no custom touch dispatch of down event", null); 441 } 442 443 @Override 444 public void dispatchTouchEventComplete() { 445 mFalsingCollector.onMotionEventComplete(); 446 } 447 448 @Override 449 public boolean shouldInterceptTouchEvent(MotionEvent ev) { 450 boolean intercepted = shouldInterceptTouchEventInternal(ev); 451 if (intercepted) { 452 mUseDragDownHelperForTouch = mLastInterceptWasDragDownHelper; 453 } 454 return intercepted; 455 } 456 457 private boolean shouldInterceptTouchEventInternal(MotionEvent ev) { 458 mLastInterceptWasDragDownHelper = false; 459 // When the device starts dozing, there's a delay before the device's display state 460 // changes from ON => DOZE to allow for the light reveal animation to run at 461 // a higher refresh rate and to delay visual changes (ie: display blink) when 462 // changing the display state. We'll call this specific state the 463 // "aodDefermentState". In this state we: 464 // - don't want touches to get sent to underlying views, except the lock icon 465 // - handle the tap to wake gesture via the PulsingGestureListener 466 if (mStatusBarStateController.isDozing() 467 && !mDozeServiceHost.isPulsing() 468 && !mDockManager.isDocked() 469 && !mLockIconViewController.willHandleTouchWhileDozing(ev) 470 ) { 471 if (ev.getAction() == MotionEvent.ACTION_DOWN) { 472 mShadeLogger.d("NSWVC: capture all touch events in always-on"); 473 } 474 return true; 475 } 476 477 if (mStatusBarKeyguardViewManager.shouldInterceptTouchEvent(ev)) { 478 // Don't allow touches to proceed to underlying views if alternate 479 // bouncer is showing 480 if (ev.getAction() == MotionEvent.ACTION_DOWN) { 481 mShadeLogger.d("NSWVC: alt bouncer showing"); 482 } 483 return true; 484 } 485 486 boolean bouncerShowing; 487 if (DeviceEntryUdfpsRefactor.isEnabled()) { 488 bouncerShowing = mPrimaryBouncerInteractor.isBouncerShowing() 489 || mAlternateBouncerInteractor.isVisibleState(); 490 } else { 491 bouncerShowing = mService.isBouncerShowing(); 492 } 493 if (mPanelExpansionInteractor.isFullyExpanded() 494 && !bouncerShowing 495 && !mStatusBarStateController.isDozing()) { 496 if (mDragDownHelper.isDragDownEnabled()) { 497 if (MigrateClocksToBlueprint.isEnabled()) { 498 // When on lockscreen, if the touch originates at the top of the screen 499 // go directly to QS and not the shade 500 if (mStatusBarStateController.getState() == KEYGUARD 501 && mQuickSettingsController.shouldQuickSettingsIntercept( 502 ev.getX(), ev.getY(), 0)) { 503 mShadeLogger.d("NSWVC: QS intercepted"); 504 return true; 505 } 506 } 507 508 // This handles drag down over lockscreen 509 boolean result = mDragDownHelper.onInterceptTouchEvent(ev); 510 if (MigrateClocksToBlueprint.isEnabled()) { 511 if (result) { 512 mLastInterceptWasDragDownHelper = true; 513 if (ev.getAction() == MotionEvent.ACTION_DOWN) { 514 mShadeLogger.d("NSWVC: drag down helper intercepted"); 515 } 516 } else if (didNotificationPanelInterceptEvent(ev)) { 517 return true; 518 } 519 } else { 520 if (result) { 521 if (ev.getAction() == MotionEvent.ACTION_DOWN) { 522 mShadeLogger.d("NSWVC: drag down helper intercepted"); 523 } 524 } 525 } 526 return result; 527 } else { 528 // This else handles interactions on the full shade while unlocked 529 if (didNotificationPanelInterceptEvent(ev)) { 530 return true; 531 } 532 } 533 } else if (MigrateClocksToBlueprint.isEnabled()) { 534 // This final check handles swipes on HUNs and when Pulsing 535 if (!bouncerShowing && didNotificationPanelInterceptEvent(ev)) { 536 mShadeLogger.d("NSWVC: intercepted for HUN/PULSING"); 537 return true; 538 } 539 } 540 return false; 541 } 542 543 @Override 544 public void didIntercept(MotionEvent ev) { 545 MotionEvent cancellation = MotionEvent.obtain(ev); 546 cancellation.setAction(MotionEvent.ACTION_CANCEL); 547 mStackScrollLayout.onInterceptTouchEvent(cancellation); 548 if (!MigrateClocksToBlueprint.isEnabled()) { 549 mShadeViewController.handleExternalInterceptTouch(cancellation); 550 } 551 cancellation.recycle(); 552 } 553 554 @Override 555 public boolean handleTouchEvent(MotionEvent ev) { 556 boolean handled = false; 557 if (mStatusBarStateController.isDozing()) { 558 handled = !mDozeServiceHost.isPulsing(); 559 } 560 if (mStatusBarKeyguardViewManager.onTouch(ev)) { 561 return true; 562 } 563 if (MigrateClocksToBlueprint.isEnabled()) { 564 if (mLastInterceptWasDragDownHelper && (mDragDownHelper.isDraggingDown())) { 565 // we still want to finish our drag down gesture when locking the screen 566 handled |= mDragDownHelper.onTouchEvent(ev) || handled; 567 } 568 if (!handled && mShadeViewController.handleExternalTouch(ev)) { 569 return true; 570 } 571 } else { 572 if (mDragDownHelper.isDragDownEnabled() 573 || mDragDownHelper.isDraggingDown()) { 574 // we still want to finish our drag down gesture when locking the screen 575 return mDragDownHelper.onTouchEvent(ev) || handled; 576 } else { 577 return handled; 578 } 579 } 580 return handled; 581 } 582 583 @Override 584 public void didNotHandleTouchEvent(MotionEvent ev) { 585 final int action = ev.getActionMasked(); 586 if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) { 587 mService.setInteracting(StatusBarManager.WINDOW_STATUS_BAR, false); 588 } 589 } 590 591 @Override 592 public boolean interceptMediaKey(KeyEvent event) { 593 return mSysUIKeyEventHandler.interceptMediaKey(event); 594 } 595 596 @Override 597 public boolean dispatchKeyEventPreIme(KeyEvent event) { 598 return mSysUIKeyEventHandler.dispatchKeyEventPreIme(event); 599 } 600 601 @Override 602 public boolean dispatchKeyEvent(KeyEvent event) { 603 return mSysUIKeyEventHandler.dispatchKeyEvent(event); 604 } 605 606 @Override 607 public void collectKeyEvent(KeyEvent event) { 608 mFalsingCollector.onKeyEvent(event); 609 } 610 }); 611 612 mView.setOnHierarchyChangeListener(new ViewGroup.OnHierarchyChangeListener() { 613 @Override 614 public void onChildViewAdded(View parent, View child) { 615 if (child.getId() == R.id.brightness_mirror_container) { 616 mBrightnessMirror = child; 617 } 618 } 619 620 @Override 621 public void onChildViewRemoved(View parent, View child) { 622 } 623 }); 624 625 setDragDownHelper(mLockscreenShadeTransitionController.getTouchHelper()); 626 627 mDepthController.setRoot(mView); 628 ShadeExpansionChangeEvent currentState = 629 mShadeExpansionStateManager.addExpansionListener(mDepthController); 630 mDepthController.onPanelExpansionChanged(currentState); 631 } 632 633 /** 634 * Sets up the glanceable hub UI if the {@link com.android.systemui.Flags#FLAG_COMMUNAL_HUB} 635 * flag is enabled. 636 * 637 * The layout lives in {@link R.id.communal_ui_stub}. 638 */ setupCommunalHubLayout()639 public void setupCommunalHubLayout() { 640 if (SceneContainerFlag.isEnabled()) { 641 // GlanceableHubContainerController is only used pre-flexiglass. 642 return; 643 } 644 collectFlow( 645 mView, 646 mGlanceableHubContainerController.communalAvailable(), 647 isEnabled -> { 648 if (isEnabled) { 649 View communalPlaceholder = mView.findViewById(R.id.communal_ui_stub); 650 int index = mView.indexOfChild(communalPlaceholder); 651 mView.addView( 652 mGlanceableHubContainerController.initView(mView.getContext()), 653 index); 654 } else { 655 mGlanceableHubContainerController.disposeView(); 656 } 657 } 658 ); 659 } 660 didNotificationPanelInterceptEvent(MotionEvent ev)661 private boolean didNotificationPanelInterceptEvent(MotionEvent ev) { 662 if (MigrateClocksToBlueprint.isEnabled()) { 663 // Since NotificationStackScrollLayout is now a sibling of notification_panel, we need 664 // to also ask NotificationPanelViewController directly, in order to process swipe up 665 // events originating from notifications 666 if (mShadeViewController.handleExternalInterceptTouch(ev)) { 667 mShadeLogger.d("NSWVC: NPVC intercepted"); 668 return true; 669 } 670 } 671 672 return false; 673 } 674 getView()675 public NotificationShadeWindowView getView() { 676 return mView; 677 } 678 cancelCurrentTouch()679 public void cancelCurrentTouch() { 680 mShadeLogger.d("NSWVC: cancelling current touch"); 681 if (mTouchActive) { 682 final long now = mClock.uptimeMillis(); 683 final MotionEvent event; 684 event = MotionEvent.obtain(mDownEvent); 685 event.setDownTime(now); 686 event.setAction(MotionEvent.ACTION_CANCEL); 687 event.setLocation(0.0f, 0.0f); 688 Log.w(TAG, "Canceling current touch event (should be very rare)"); 689 mView.dispatchTouchEvent(event); 690 event.recycle(); 691 mTouchCancelled = true; 692 } 693 mAmbientState.setSwipingUp(false); 694 if (MigrateClocksToBlueprint.isEnabled()) { 695 mDragDownHelper.stopDragging(); 696 } 697 } 698 699 @Override dump(PrintWriter pw, String[] args)700 public void dump(PrintWriter pw, String[] args) { 701 pw.print(" mExpandAnimationRunning="); 702 pw.println(mExpandAnimationRunning); 703 pw.print(" mTouchCancelled="); 704 pw.println(mTouchCancelled); 705 pw.print(" mTouchActive="); 706 pw.println(mTouchActive); 707 } 708 709 @VisibleForTesting setExpandAnimationRunning(boolean running)710 void setExpandAnimationRunning(boolean running) { 711 if (mExpandAnimationRunning != running) { 712 // TODO(b/288507023): Remove this log. 713 if (ActivityTransitionAnimator.DEBUG_TRANSITION_ANIMATION) { 714 Log.d(TAG, "Setting mExpandAnimationRunning=" + running); 715 } 716 if (running) { 717 mLaunchAnimationTimeout = mClock.uptimeMillis() + 5000; 718 } 719 mExpandAnimationRunning = running; 720 mNotificationShadeWindowController.setLaunchingActivity(mExpandAnimationRunning); 721 } 722 } 723 cancelExpandHelper()724 public void cancelExpandHelper() { 725 if (mStackScrollLayout != null) { 726 mStackScrollLayout.cancelExpandHelper(); 727 } 728 } 729 setStatusBarViewController(PhoneStatusBarViewController statusBarViewController)730 public void setStatusBarViewController(PhoneStatusBarViewController statusBarViewController) { 731 mStatusBarViewController = statusBarViewController; 732 } 733 734 @VisibleForTesting setDragDownHelper(DragDownHelper dragDownHelper)735 void setDragDownHelper(DragDownHelper dragDownHelper) { 736 mDragDownHelper = dragDownHelper; 737 } 738 } 739