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.flags.Flags.TRACKPAD_GESTURE_COMMON;
21 import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
22 
23 import android.app.StatusBarManager;
24 import android.os.PowerManager;
25 import android.util.Log;
26 import android.view.GestureDetector;
27 import android.view.InputDevice;
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.KeyguardMessageAreaController;
36 import com.android.keyguard.LockIconViewController;
37 import com.android.keyguard.dagger.KeyguardBouncerComponent;
38 import com.android.systemui.R;
39 import com.android.systemui.animation.ActivityLaunchAnimator;
40 import com.android.systemui.back.domain.interactor.BackActionInteractor;
41 import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor;
42 import com.android.systemui.bouncer.ui.binder.KeyguardBouncerViewBinder;
43 import com.android.systemui.bouncer.ui.viewmodel.KeyguardBouncerViewModel;
44 import com.android.systemui.classifier.FalsingCollector;
45 import com.android.systemui.dagger.SysUISingleton;
46 import com.android.systemui.dock.DockManager;
47 import com.android.systemui.flags.FeatureFlags;
48 import com.android.systemui.flags.Flags;
49 import com.android.systemui.keyevent.domain.interactor.KeyEventInteractor;
50 import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
51 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
52 import com.android.systemui.keyguard.shared.model.TransitionState;
53 import com.android.systemui.keyguard.shared.model.TransitionStep;
54 import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel;
55 import com.android.systemui.log.BouncerLogger;
56 import com.android.systemui.power.domain.interactor.PowerInteractor;
57 import com.android.systemui.shared.animation.DisableSubpixelTextTransitionListener;
58 import com.android.systemui.statusbar.DragDownHelper;
59 import com.android.systemui.statusbar.LockscreenShadeTransitionController;
60 import com.android.systemui.statusbar.NotificationInsetsController;
61 import com.android.systemui.statusbar.NotificationShadeDepthController;
62 import com.android.systemui.statusbar.NotificationShadeWindowController;
63 import com.android.systemui.statusbar.SysuiStatusBarStateController;
64 import com.android.systemui.statusbar.notification.data.repository.NotificationExpansionRepository;
65 import com.android.systemui.statusbar.notification.stack.AmbientState;
66 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
67 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
68 import com.android.systemui.statusbar.phone.CentralSurfaces;
69 import com.android.systemui.statusbar.phone.PhoneStatusBarViewController;
70 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
71 import com.android.systemui.statusbar.window.StatusBarWindowStateController;
72 import com.android.systemui.unfold.UnfoldTransitionProgressProvider;
73 import com.android.systemui.util.time.SystemClock;
74 
75 import java.io.PrintWriter;
76 import java.util.Optional;
77 import java.util.function.Consumer;
78 
79 import javax.inject.Inject;
80 
81 /**
82  * Controller for {@link NotificationShadeWindowView}.
83  */
84 @SysUISingleton
85 public class NotificationShadeWindowViewController {
86     private static final String TAG = "NotifShadeWindowVC";
87     private final FalsingCollector mFalsingCollector;
88     private final SysuiStatusBarStateController mStatusBarStateController;
89     private final NotificationShadeWindowView mView;
90     private final NotificationShadeDepthController mDepthController;
91     private final NotificationStackScrollLayoutController mNotificationStackScrollLayoutController;
92     private final LockscreenShadeTransitionController mLockscreenShadeTransitionController;
93     private final LockIconViewController mLockIconViewController;
94     private final ShadeLogger mShadeLogger;
95     private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
96     private final StatusBarWindowStateController mStatusBarWindowStateController;
97     private final KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
98     private final AmbientState mAmbientState;
99     private final PulsingGestureListener mPulsingGestureListener;
100     private final LockscreenHostedDreamGestureListener mLockscreenHostedDreamGestureListener;
101     private final NotificationInsetsController mNotificationInsetsController;
102     private final boolean mIsTrackpadCommonEnabled;
103     private final FeatureFlags mFeatureFlags;
104     private final KeyEventInteractor mKeyEventInteractor;
105     private GestureDetector mPulsingWakeupGestureHandler;
106     private GestureDetector mDreamingWakeupGestureHandler;
107     private View mBrightnessMirror;
108     private boolean mTouchActive;
109     private boolean mTouchCancelled;
110     private MotionEvent mDownEvent;
111     // TODO rename to mLaunchAnimationRunning
112     private boolean mExpandAnimationRunning;
113     /**
114      *  When mExpandAnimationRunning is true and the touch dispatcher receives a down even after
115      *  uptime exceeds this, the dispatcher will stop blocking touches for the launch animation,
116      *  which has presumabely not completed due to an error.
117      */
118     private long mLaunchAnimationTimeout;
119     private NotificationStackScrollLayout mStackScrollLayout;
120     private PhoneStatusBarViewController mStatusBarViewController;
121     private final CentralSurfaces mService;
122     private final BackActionInteractor mBackActionInteractor;
123     private final PowerInteractor mPowerInteractor;
124     private final NotificationShadeWindowController mNotificationShadeWindowController;
125     private DragDownHelper mDragDownHelper;
126     private boolean mExpandingBelowNotch;
127     private final DockManager mDockManager;
128     private final NotificationPanelViewController mNotificationPanelViewController;
129     private final ShadeExpansionStateManager mShadeExpansionStateManager;
130 
131     private boolean mIsTrackingBarGesture = false;
132     private boolean mIsOcclusionTransitionRunning = false;
133     private DisableSubpixelTextTransitionListener mDisableSubpixelTextTransitionListener;
134     private final Consumer<TransitionStep> mLockscreenToDreamingTransition =
135             (TransitionStep step) -> {
136                 mIsOcclusionTransitionRunning =
137                     step.getTransitionState() == TransitionState.RUNNING;
138             };
139     private final SystemClock mClock;
140 
141     @Inject
NotificationShadeWindowViewController( LockscreenShadeTransitionController transitionController, FalsingCollector falsingCollector, SysuiStatusBarStateController statusBarStateController, DockManager dockManager, NotificationShadeDepthController depthController, NotificationShadeWindowView notificationShadeWindowView, NotificationPanelViewController notificationPanelViewController, ShadeExpansionStateManager shadeExpansionStateManager, NotificationStackScrollLayoutController notificationStackScrollLayoutController, StatusBarKeyguardViewManager statusBarKeyguardViewManager, StatusBarWindowStateController statusBarWindowStateController, LockIconViewController lockIconViewController, CentralSurfaces centralSurfaces, BackActionInteractor backActionInteractor, PowerInteractor powerInteractor, NotificationShadeWindowController controller, Optional<UnfoldTransitionProgressProvider> unfoldTransitionProgressProvider, KeyguardUnlockAnimationController keyguardUnlockAnimationController, NotificationInsetsController notificationInsetsController, AmbientState ambientState, ShadeLogger shadeLogger, PulsingGestureListener pulsingGestureListener, LockscreenHostedDreamGestureListener lockscreenHostedDreamGestureListener, KeyguardBouncerViewModel keyguardBouncerViewModel, KeyguardBouncerComponent.Factory keyguardBouncerComponentFactory, KeyguardMessageAreaController.Factory messageAreaControllerFactory, KeyguardTransitionInteractor keyguardTransitionInteractor, PrimaryBouncerToGoneTransitionViewModel primaryBouncerToGoneTransitionViewModel, NotificationExpansionRepository notificationExpansionRepository, FeatureFlags featureFlags, SystemClock clock, BouncerMessageInteractor bouncerMessageInteractor, BouncerLogger bouncerLogger, KeyEventInteractor keyEventInteractor)142     public NotificationShadeWindowViewController(
143             LockscreenShadeTransitionController transitionController,
144             FalsingCollector falsingCollector,
145             SysuiStatusBarStateController statusBarStateController,
146             DockManager dockManager,
147             NotificationShadeDepthController depthController,
148             NotificationShadeWindowView notificationShadeWindowView,
149             NotificationPanelViewController notificationPanelViewController,
150             ShadeExpansionStateManager shadeExpansionStateManager,
151             NotificationStackScrollLayoutController notificationStackScrollLayoutController,
152             StatusBarKeyguardViewManager statusBarKeyguardViewManager,
153             StatusBarWindowStateController statusBarWindowStateController,
154             LockIconViewController lockIconViewController,
155             CentralSurfaces centralSurfaces,
156             BackActionInteractor backActionInteractor,
157             PowerInteractor powerInteractor,
158             NotificationShadeWindowController controller,
159             Optional<UnfoldTransitionProgressProvider> unfoldTransitionProgressProvider,
160             KeyguardUnlockAnimationController keyguardUnlockAnimationController,
161             NotificationInsetsController notificationInsetsController,
162             AmbientState ambientState,
163             ShadeLogger shadeLogger,
164             PulsingGestureListener pulsingGestureListener,
165             LockscreenHostedDreamGestureListener lockscreenHostedDreamGestureListener,
166             KeyguardBouncerViewModel keyguardBouncerViewModel,
167             KeyguardBouncerComponent.Factory keyguardBouncerComponentFactory,
168             KeyguardMessageAreaController.Factory messageAreaControllerFactory,
169             KeyguardTransitionInteractor keyguardTransitionInteractor,
170             PrimaryBouncerToGoneTransitionViewModel primaryBouncerToGoneTransitionViewModel,
171             NotificationExpansionRepository notificationExpansionRepository,
172             FeatureFlags featureFlags,
173             SystemClock clock,
174             BouncerMessageInteractor bouncerMessageInteractor,
175             BouncerLogger bouncerLogger,
176             KeyEventInteractor keyEventInteractor) {
177         mLockscreenShadeTransitionController = transitionController;
178         mFalsingCollector = falsingCollector;
179         mStatusBarStateController = statusBarStateController;
180         mView = notificationShadeWindowView;
181         mDockManager = dockManager;
182         mNotificationPanelViewController = notificationPanelViewController;
183         mShadeExpansionStateManager = shadeExpansionStateManager;
184         mDepthController = depthController;
185         mNotificationStackScrollLayoutController = notificationStackScrollLayoutController;
186         mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
187         mStatusBarWindowStateController = statusBarWindowStateController;
188         mLockIconViewController = lockIconViewController;
189         mBackActionInteractor = backActionInteractor;
190         mShadeLogger = shadeLogger;
191         mLockIconViewController.init();
192         mService = centralSurfaces;
193         mPowerInteractor = powerInteractor;
194         mNotificationShadeWindowController = controller;
195         mKeyguardUnlockAnimationController = keyguardUnlockAnimationController;
196         mAmbientState = ambientState;
197         mPulsingGestureListener = pulsingGestureListener;
198         mLockscreenHostedDreamGestureListener = lockscreenHostedDreamGestureListener;
199         mNotificationInsetsController = notificationInsetsController;
200         mIsTrackpadCommonEnabled = featureFlags.isEnabled(TRACKPAD_GESTURE_COMMON);
201         mFeatureFlags = featureFlags;
202         mKeyEventInteractor = keyEventInteractor;
203 
204         // This view is not part of the newly inflated expanded status bar.
205         mBrightnessMirror = mView.findViewById(R.id.brightness_mirror_container);
206         mDisableSubpixelTextTransitionListener = new DisableSubpixelTextTransitionListener(mView);
207         KeyguardBouncerViewBinder.bind(
208                 mView.findViewById(R.id.keyguard_bouncer_container),
209                 keyguardBouncerViewModel,
210                 primaryBouncerToGoneTransitionViewModel,
211                 keyguardBouncerComponentFactory,
212                 messageAreaControllerFactory,
213                 bouncerMessageInteractor,
214                 bouncerLogger,
215                 featureFlags);
216 
217         collectFlow(mView, keyguardTransitionInteractor.getLockscreenToDreamingTransition(),
218                 mLockscreenToDreamingTransition);
219         collectFlow(
220                 mView,
221                 notificationExpansionRepository.isExpandAnimationRunning(),
222                 this::setExpandAnimationRunning);
223 
224         mClock = clock;
225         if (featureFlags.isEnabled(Flags.SPLIT_SHADE_SUBPIXEL_OPTIMIZATION)) {
226             unfoldTransitionProgressProvider.ifPresent(
227                     progressProvider -> progressProvider.addCallback(
228                             mDisableSubpixelTextTransitionListener));
229         }
230     }
231 
232     /**
233      * @return Location where to place the KeyguardMessageArea
234      */
getKeyguardMessageArea()235     public AuthKeyguardMessageArea getKeyguardMessageArea() {
236         return mView.findViewById(R.id.keyguard_message_area);
237     }
238 
logDownDispatch(MotionEvent ev, String msg, Boolean result)239     private Boolean logDownDispatch(MotionEvent ev, String msg, Boolean result) {
240         if (ev.getAction() == MotionEvent.ACTION_DOWN) {
241             mShadeLogger.logShadeWindowDispatch(ev, msg, result);
242         }
243         return result;
244     }
245 
246     /** Inflates the {@link R.layout#status_bar_expanded} layout and sets it up. */
setupExpandedStatusBar()247     public void setupExpandedStatusBar() {
248         mStackScrollLayout = mView.findViewById(R.id.notification_stack_scroller);
249         mPulsingWakeupGestureHandler = new GestureDetector(mView.getContext(),
250                 mPulsingGestureListener);
251         if (mFeatureFlags.isEnabled(LOCKSCREEN_WALLPAPER_DREAM_ENABLED)) {
252             mDreamingWakeupGestureHandler = new GestureDetector(mView.getContext(),
253                     mLockscreenHostedDreamGestureListener);
254         }
255         mView.setLayoutInsetsController(mNotificationInsetsController);
256         mView.setInteractionEventHandler(new NotificationShadeWindowView.InteractionEventHandler() {
257             @Override
258             public Boolean handleDispatchTouchEvent(MotionEvent ev) {
259                 if (mStatusBarViewController == null) { // Fix for b/192490822
260                     return logDownDispatch(ev,
261                             "Ignoring touch while statusBarView not yet set", false);
262                 }
263                 boolean isDown = ev.getActionMasked() == MotionEvent.ACTION_DOWN;
264                 boolean isUp = ev.getActionMasked() == MotionEvent.ACTION_UP;
265                 boolean isCancel = ev.getActionMasked() == MotionEvent.ACTION_CANCEL;
266 
267                 boolean expandingBelowNotch = mExpandingBelowNotch;
268                 if (isUp || isCancel) {
269                     mExpandingBelowNotch = false;
270                 }
271 
272                 // Reset manual touch dispatch state here but make sure the UP/CANCEL event still
273                 // gets delivered.
274                 if (!isCancel && mService.shouldIgnoreTouch()) {
275                     return logDownDispatch(ev, "touch ignored by CS", false);
276                 }
277 
278                 if (isDown) {
279                     mTouchActive = true;
280                     mTouchCancelled = false;
281                     mDownEvent = ev;
282                 } else if (ev.getActionMasked() == MotionEvent.ACTION_UP
283                         || ev.getActionMasked() == MotionEvent.ACTION_CANCEL) {
284                     mTouchActive = false;
285                     mDownEvent = null;
286                 }
287                 if (mTouchCancelled) {
288                     return logDownDispatch(ev, "touch cancelled", false);
289                 }
290                 if (mExpandAnimationRunning) {
291                     if (isDown && mClock.uptimeMillis() > mLaunchAnimationTimeout) {
292                         Log.wtf(TAG, "NSWVC: launch animation timed out");
293                         setExpandAnimationRunning(false);
294                     } else {
295                         return logDownDispatch(ev, "expand animation running", false);
296                     }
297                 }
298 
299                 if (mKeyguardUnlockAnimationController.isPlayingCannedUnlockAnimation()) {
300                     // If the user was sliding their finger across the lock screen,
301                     // we may have been intercepting the touch and forwarding it to the
302                     // UDFPS affordance via mStatusBarKeyguardViewManager.onTouch (see below).
303                     // If this touch ended up unlocking the device, we want to cancel the touch
304                     // immediately, so we don't cause swipe or expand animations afterwards.
305                     cancelCurrentTouch();
306                     return true;
307                 }
308 
309                 if (mIsOcclusionTransitionRunning) {
310                     return logDownDispatch(ev, "occlusion transition running", false);
311                 }
312 
313                 mFalsingCollector.onTouchEvent(ev);
314                 mPulsingWakeupGestureHandler.onTouchEvent(ev);
315                 if (mDreamingWakeupGestureHandler != null
316                         && mDreamingWakeupGestureHandler.onTouchEvent(ev)) {
317                     return logDownDispatch(ev, "dream wakeup gesture handled", true);
318                 }
319                 if (mStatusBarKeyguardViewManager.dispatchTouchEvent(ev)) {
320                     return logDownDispatch(ev, "dispatched to Keyguard", true);
321                 }
322                 if (mBrightnessMirror != null
323                         && mBrightnessMirror.getVisibility() == View.VISIBLE) {
324                     // Disallow new pointers while the brightness mirror is visible. This is so that
325                     // you can't touch anything other than the brightness slider while the mirror is
326                     // showing and the rest of the panel is transparent.
327                     if (ev.getActionMasked() == MotionEvent.ACTION_POINTER_DOWN) {
328                         return logDownDispatch(ev, "disallowed new pointer", false);
329                     }
330                 }
331                 if (isDown) {
332                     mNotificationStackScrollLayoutController.closeControlsIfOutsideTouch(ev);
333                 }
334 
335                 if (mStatusBarStateController.isDozing()) {
336                     mService.extendDozePulse();
337                 }
338                 mLockIconViewController.onTouchEvent(
339                         ev,
340                         /* onGestureDetectedRunnable */
341                         () -> {
342                             mService.userActivity();
343                             mPowerInteractor.wakeUpIfDozing(
344                                     "LOCK_ICON_TOUCH",
345                                     PowerManager.WAKE_REASON_GESTURE);
346                         }
347                 );
348 
349                 // In case we start outside of the view bounds (below the status bar), we need to
350                 // dispatch
351                 // the touch manually as the view system can't accommodate for touches outside of
352                 // the
353                 // regular view bounds.
354                 if (isDown && ev.getY() >= mView.getBottom()) {
355                     mExpandingBelowNotch = true;
356                     expandingBelowNotch = true;
357                 }
358                 if (expandingBelowNotch) {
359                     return logDownDispatch(ev,
360                             "expand below notch. sending touch to status bar",
361                             mStatusBarViewController.sendTouchToView(ev));
362                 }
363 
364                 if (!mIsTrackingBarGesture && isDown
365                         && mNotificationPanelViewController.isFullyCollapsed()) {
366                     float x = ev.getRawX();
367                     float y = ev.getRawY();
368                     if (mStatusBarViewController.touchIsWithinView(x, y)) {
369                         if (mStatusBarWindowStateController.windowIsShowing()) {
370                             mIsTrackingBarGesture = true;
371                             return logDownDispatch(ev, "sending touch to status bar",
372                                     mStatusBarViewController.sendTouchToView(ev));
373                         } else {
374                             return logDownDispatch(ev, "hidden or hiding", true);
375                         }
376                     }
377                 } else if (mIsTrackingBarGesture) {
378                     final boolean sendToStatusBar = mStatusBarViewController.sendTouchToView(ev);
379                     if (isUp || isCancel) {
380                         mIsTrackingBarGesture = false;
381                     }
382                     return logDownDispatch(ev, "sending bar gesture to status bar",
383                             sendToStatusBar);
384                 }
385                 return logDownDispatch(ev, "no custom touch dispatch of down event", null);
386             }
387 
388             @Override
389             public void dispatchTouchEventComplete() {
390                 mFalsingCollector.onMotionEventComplete();
391             }
392 
393             @Override
394             public boolean shouldInterceptTouchEvent(MotionEvent ev) {
395                 if (mStatusBarStateController.isDozing() && !mService.isPulsing()
396                         && !mDockManager.isDocked()) {
397                     if (ev.getAction() == MotionEvent.ACTION_DOWN) {
398                         mShadeLogger.d("NSWVC: capture all touch events in always-on");
399                     }
400                     return true;
401                 }
402 
403                 if (mStatusBarKeyguardViewManager.shouldInterceptTouchEvent(ev)) {
404                     // Don't allow touches to proceed to underlying views if alternate
405                     // bouncer is showing
406                     if (ev.getAction() == MotionEvent.ACTION_DOWN) {
407                         mShadeLogger.d("NSWVC: alt bouncer showing");
408                     }
409                     return true;
410                 }
411 
412                 if (mLockIconViewController.onInterceptTouchEvent(ev)) {
413                     // immediately return true; don't send the touch to the drag down helper
414                     if (ev.getAction() == MotionEvent.ACTION_DOWN) {
415                         mShadeLogger.d("NSWVC: don't send touch to drag down helper");
416                     }
417                     return true;
418                 }
419 
420                 if (mNotificationPanelViewController.isFullyExpanded()
421                         && mDragDownHelper.isDragDownEnabled()
422                         && !mService.isBouncerShowing()
423                         && !mStatusBarStateController.isDozing()) {
424                     boolean result = mDragDownHelper.onInterceptTouchEvent(ev);
425                     if (result) {
426                         if (ev.getAction() == MotionEvent.ACTION_DOWN) {
427                             mShadeLogger.d("NSWVC: drag down helper intercepted");
428                         }
429                     }
430                     return result;
431                 } else {
432                     return false;
433                 }
434             }
435 
436             @Override
437             public void didIntercept(MotionEvent ev) {
438                 MotionEvent cancellation = MotionEvent.obtain(ev);
439                 cancellation.setAction(MotionEvent.ACTION_CANCEL);
440                 mStackScrollLayout.onInterceptTouchEvent(cancellation);
441                 mNotificationPanelViewController.handleExternalInterceptTouch(cancellation);
442                 cancellation.recycle();
443             }
444 
445             @Override
446             public boolean handleTouchEvent(MotionEvent ev) {
447                 boolean handled = false;
448                 if (mStatusBarStateController.isDozing()) {
449                     handled = !mService.isPulsing();
450                 }
451 
452                 if (mStatusBarKeyguardViewManager.onTouch(ev)) {
453                     return true;
454                 }
455 
456                 if (mDragDownHelper.isDragDownEnabled()
457                         || mDragDownHelper.isDraggingDown()) {
458                     // we still want to finish our drag down gesture when locking the screen
459                     return mDragDownHelper.onTouchEvent(ev) || handled;
460                 } else {
461                     return handled;
462                 }
463             }
464 
465             @Override
466             public void didNotHandleTouchEvent(MotionEvent ev) {
467                 final int action = ev.getActionMasked();
468                 if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
469                     mService.setInteracting(StatusBarManager.WINDOW_STATUS_BAR, false);
470                 }
471             }
472 
473             @Override
474             public boolean interceptMediaKey(KeyEvent event) {
475                 return mKeyEventInteractor.interceptMediaKey(event);
476             }
477 
478             @Override
479             public boolean dispatchKeyEventPreIme(KeyEvent event) {
480                 return mKeyEventInteractor.dispatchKeyEventPreIme(event);
481             }
482 
483             @Override
484             public boolean dispatchKeyEvent(KeyEvent event) {
485                 return mKeyEventInteractor.dispatchKeyEvent(event);
486             }
487         });
488 
489         mView.setOnHierarchyChangeListener(new ViewGroup.OnHierarchyChangeListener() {
490             @Override
491             public void onChildViewAdded(View parent, View child) {
492                 if (child.getId() == R.id.brightness_mirror_container) {
493                     mBrightnessMirror = child;
494                 }
495             }
496 
497             @Override
498             public void onChildViewRemoved(View parent, View child) {
499             }
500         });
501 
502         setDragDownHelper(mLockscreenShadeTransitionController.getTouchHelper());
503 
504         mDepthController.setRoot(mView);
505         ShadeExpansionChangeEvent currentState =
506                 mShadeExpansionStateManager.addExpansionListener(mDepthController);
507         mDepthController.onPanelExpansionChanged(currentState);
508     }
509 
getView()510     public NotificationShadeWindowView getView() {
511         return mView;
512     }
513 
cancelCurrentTouch()514     public void cancelCurrentTouch() {
515         mShadeLogger.d("NSWVC: cancelling current touch");
516         if (mTouchActive) {
517             final long now = mClock.uptimeMillis();
518             final MotionEvent event;
519             if (mIsTrackpadCommonEnabled) {
520                 event = MotionEvent.obtain(mDownEvent);
521                 event.setDownTime(now);
522                 event.setAction(MotionEvent.ACTION_CANCEL);
523                 event.setLocation(0.0f, 0.0f);
524             } else {
525                 event = MotionEvent.obtain(now, now,
526                         MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
527                 event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
528             }
529             Log.w(TAG, "Canceling current touch event (should be very rare)");
530             mView.dispatchTouchEvent(event);
531             event.recycle();
532             mTouchCancelled = true;
533         }
534         mAmbientState.setSwipingUp(false);
535     }
536 
dump(PrintWriter pw, String[] args)537     public void dump(PrintWriter pw, String[] args) {
538         pw.print("  mExpandAnimationRunning=");
539         pw.println(mExpandAnimationRunning);
540         pw.print("  mTouchCancelled=");
541         pw.println(mTouchCancelled);
542         pw.print("  mTouchActive=");
543         pw.println(mTouchActive);
544     }
545 
546     @VisibleForTesting
setExpandAnimationRunning(boolean running)547     void setExpandAnimationRunning(boolean running) {
548         if (mExpandAnimationRunning != running) {
549             // TODO(b/288507023): Remove this log.
550             if (ActivityLaunchAnimator.DEBUG_LAUNCH_ANIMATION) {
551                 Log.d(TAG, "Setting mExpandAnimationRunning=" + running);
552             }
553             if (running) {
554                 mLaunchAnimationTimeout = mClock.uptimeMillis() + 5000;
555             }
556             mExpandAnimationRunning = running;
557             mNotificationShadeWindowController.setLaunchingActivity(mExpandAnimationRunning);
558         }
559     }
560 
cancelExpandHelper()561     public void cancelExpandHelper() {
562         if (mStackScrollLayout != null) {
563             mStackScrollLayout.cancelExpandHelper();
564         }
565     }
566 
setStatusBarViewController(PhoneStatusBarViewController statusBarViewController)567     public void setStatusBarViewController(PhoneStatusBarViewController statusBarViewController) {
568         mStatusBarViewController = statusBarViewController;
569     }
570 
571     @VisibleForTesting
setDragDownHelper(DragDownHelper dragDownHelper)572     void setDragDownHelper(DragDownHelper dragDownHelper) {
573         mDragDownHelper = dragDownHelper;
574     }
575 }
576