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