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.statusbar.phone;
18 
19 import static android.view.View.GONE;
20 
21 import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters;
22 import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL;
23 
24 import static java.lang.Float.isNaN;
25 
26 import android.animation.Animator;
27 import android.animation.AnimatorListenerAdapter;
28 import android.animation.ValueAnimator;
29 import android.app.ActivityManager;
30 import android.app.Fragment;
31 import android.app.StatusBarManager;
32 import android.content.pm.ResolveInfo;
33 import android.content.res.Configuration;
34 import android.graphics.Canvas;
35 import android.graphics.Color;
36 import android.graphics.ColorFilter;
37 import android.graphics.Paint;
38 import android.graphics.PointF;
39 import android.graphics.Rect;
40 import android.graphics.Region;
41 import android.graphics.drawable.Drawable;
42 import android.hardware.biometrics.BiometricSourceType;
43 import android.os.Bundle;
44 import android.os.PowerManager;
45 import android.os.SystemClock;
46 import android.util.Log;
47 import android.util.MathUtils;
48 import android.view.LayoutInflater;
49 import android.view.MotionEvent;
50 import android.view.VelocityTracker;
51 import android.view.View;
52 import android.view.ViewGroup;
53 import android.view.ViewPropertyAnimator;
54 import android.view.ViewTreeObserver;
55 import android.view.WindowInsets;
56 import android.view.accessibility.AccessibilityManager;
57 import android.view.accessibility.AccessibilityNodeInfo;
58 import android.widget.FrameLayout;
59 import android.widget.TextView;
60 
61 import com.android.internal.annotations.VisibleForTesting;
62 import com.android.internal.logging.MetricsLogger;
63 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
64 import com.android.internal.util.LatencyTracker;
65 import com.android.keyguard.KeyguardClockSwitch;
66 import com.android.keyguard.KeyguardStatusView;
67 import com.android.keyguard.KeyguardUpdateMonitor;
68 import com.android.keyguard.KeyguardUpdateMonitorCallback;
69 import com.android.systemui.DejankUtils;
70 import com.android.systemui.Interpolators;
71 import com.android.systemui.R;
72 import com.android.systemui.dagger.qualifiers.DisplayId;
73 import com.android.systemui.doze.DozeLog;
74 import com.android.systemui.fragments.FragmentHostManager;
75 import com.android.systemui.fragments.FragmentHostManager.FragmentListener;
76 import com.android.systemui.media.MediaHierarchyManager;
77 import com.android.systemui.plugins.FalsingManager;
78 import com.android.systemui.plugins.qs.QS;
79 import com.android.systemui.plugins.statusbar.StatusBarStateController;
80 import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
81 import com.android.systemui.qs.QSFragment;
82 import com.android.systemui.statusbar.CommandQueue;
83 import com.android.systemui.statusbar.FlingAnimationUtils;
84 import com.android.systemui.statusbar.GestureRecorder;
85 import com.android.systemui.statusbar.KeyguardAffordanceView;
86 import com.android.systemui.statusbar.KeyguardIndicationController;
87 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
88 import com.android.systemui.statusbar.NotificationShelf;
89 import com.android.systemui.statusbar.PulseExpansionHandler;
90 import com.android.systemui.statusbar.RemoteInputController;
91 import com.android.systemui.statusbar.StatusBarState;
92 import com.android.systemui.statusbar.SysuiStatusBarStateController;
93 import com.android.systemui.statusbar.VibratorHelper;
94 import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
95 import com.android.systemui.statusbar.notification.AnimatableProperty;
96 import com.android.systemui.statusbar.notification.ConversationNotificationManager;
97 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
98 import com.android.systemui.statusbar.notification.NotificationEntryManager;
99 import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
100 import com.android.systemui.statusbar.notification.PropertyAnimator;
101 import com.android.systemui.statusbar.notification.ViewGroupFadeHelper;
102 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
103 import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
104 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
105 import com.android.systemui.statusbar.notification.row.ExpandableView;
106 import com.android.systemui.statusbar.notification.stack.AnimationProperties;
107 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
108 import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
109 import com.android.systemui.statusbar.phone.LockscreenGestureLogger.LockscreenUiEvent;
110 import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
111 import com.android.systemui.statusbar.policy.ConfigurationController;
112 import com.android.systemui.statusbar.policy.KeyguardStateController;
113 import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
114 import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
115 import com.android.systemui.statusbar.policy.ZenModeController;
116 import com.android.systemui.util.InjectionInflationController;
117 
118 import java.io.FileDescriptor;
119 import java.io.PrintWriter;
120 import java.util.ArrayList;
121 import java.util.Collections;
122 import java.util.List;
123 import java.util.function.Consumer;
124 import java.util.function.Function;
125 
126 import javax.inject.Inject;
127 
128 @StatusBarComponent.StatusBarScope
129 public class NotificationPanelViewController extends PanelViewController {
130 
131     private static final boolean DEBUG = false;
132 
133     /**
134      * Fling expanding QS.
135      */
136     private static final int FLING_EXPAND = 0;
137 
138     /**
139      * Fling collapsing QS, potentially stopping when QS becomes QQS.
140      */
141     private static final int FLING_COLLAPSE = 1;
142 
143     /**
144      * Fling until QS is completely hidden.
145      */
146     private static final int FLING_HIDE = 2;
147     private final DozeParameters mDozeParameters;
148     private final OnHeightChangedListener mOnHeightChangedListener = new OnHeightChangedListener();
149     private final OnClickListener mOnClickListener = new OnClickListener();
150     private final OnOverscrollTopChangedListener
151             mOnOverscrollTopChangedListener =
152             new OnOverscrollTopChangedListener();
153     private final KeyguardAffordanceHelperCallback
154             mKeyguardAffordanceHelperCallback =
155             new KeyguardAffordanceHelperCallback();
156     private final OnEmptySpaceClickListener
157             mOnEmptySpaceClickListener =
158             new OnEmptySpaceClickListener();
159     private final MyOnHeadsUpChangedListener
160             mOnHeadsUpChangedListener =
161             new MyOnHeadsUpChangedListener();
162     private final HeightListener mHeightListener = new HeightListener();
163     private final ZenModeControllerCallback
164             mZenModeControllerCallback =
165             new ZenModeControllerCallback();
166     private final ConfigurationListener mConfigurationListener = new ConfigurationListener();
167     private final StatusBarStateListener mStatusBarStateListener = new StatusBarStateListener();
168     private final ExpansionCallback mExpansionCallback = new ExpansionCallback();
169     private final BiometricUnlockController mBiometricUnlockController;
170     private final NotificationPanelView mView;
171     private final MetricsLogger mMetricsLogger;
172     private final ActivityManager mActivityManager;
173     private final ZenModeController mZenModeController;
174     private final ConfigurationController mConfigurationController;
175     private final FlingAnimationUtils.Builder mFlingAnimationUtilsBuilder;
176 
177     // Cap and total height of Roboto font. Needs to be adjusted when font for the big clock is
178     // changed.
179     private static final int CAP_HEIGHT = 1456;
180     private static final int FONT_HEIGHT = 2163;
181 
182     /**
183      * Maximum time before which we will expand the panel even for slow motions when getting a
184      * touch passed over from launcher.
185      */
186     private static final int MAX_TIME_TO_OPEN_WHEN_FLINGING_FROM_LAUNCHER = 300;
187 
188     private static final String COUNTER_PANEL_OPEN = "panel_open";
189     private static final String COUNTER_PANEL_OPEN_QS = "panel_open_qs";
190     private static final String COUNTER_PANEL_OPEN_PEEK = "panel_open_peek";
191 
192     private static final Rect M_DUMMY_DIRTY_RECT = new Rect(0, 0, 1, 1);
193     private static final Rect EMPTY_RECT = new Rect();
194 
195     private static final AnimationProperties
196             CLOCK_ANIMATION_PROPERTIES =
197             new AnimationProperties().setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
198     private final AnimatableProperty KEYGUARD_HEADS_UP_SHOWING_AMOUNT = AnimatableProperty.from(
199             "KEYGUARD_HEADS_UP_SHOWING_AMOUNT",
200             (notificationPanelView, aFloat) -> setKeyguardHeadsUpShowingAmount(aFloat),
201             (Function<NotificationPanelView, Float>) notificationPanelView ->
202                     getKeyguardHeadsUpShowingAmount(),
203             R.id.keyguard_hun_animator_tag, R.id.keyguard_hun_animator_end_tag,
204             R.id.keyguard_hun_animator_start_tag);
205     private static final AnimationProperties
206             KEYGUARD_HUN_PROPERTIES =
207             new AnimationProperties().setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
208     @VisibleForTesting
209     final KeyguardUpdateMonitorCallback
210             mKeyguardUpdateCallback =
211             new KeyguardUpdateMonitorCallback() {
212 
213                 @Override
214                 public void onBiometricAuthenticated(int userId,
215                         BiometricSourceType biometricSourceType,
216                         boolean isStrongBiometric) {
217                     if (mFirstBypassAttempt
218                             && mUpdateMonitor.isUnlockingWithBiometricAllowed(isStrongBiometric)) {
219                         mDelayShowingKeyguardStatusBar = true;
220                     }
221                 }
222 
223                 @Override
224                 public void onBiometricRunningStateChanged(boolean running,
225                         BiometricSourceType biometricSourceType) {
226                     boolean
227                             keyguardOrShadeLocked =
228                             mBarState == StatusBarState.KEYGUARD
229                                     || mBarState == StatusBarState.SHADE_LOCKED;
230                     if (!running && mFirstBypassAttempt && keyguardOrShadeLocked && !mDozing
231                             && !mDelayShowingKeyguardStatusBar
232                             && !mBiometricUnlockController.isBiometricUnlock()) {
233                         mFirstBypassAttempt = false;
234                         animateKeyguardStatusBarIn(StackStateAnimator.ANIMATION_DURATION_STANDARD);
235                     }
236                 }
237 
238                 @Override
239                 public void onFinishedGoingToSleep(int why) {
240                     mFirstBypassAttempt = mKeyguardBypassController.getBypassEnabled();
241                     mDelayShowingKeyguardStatusBar = false;
242                 }
243             };
244 
245     private final InjectionInflationController mInjectionInflationController;
246     private final PowerManager mPowerManager;
247     private final AccessibilityManager mAccessibilityManager;
248     private final NotificationWakeUpCoordinator mWakeUpCoordinator;
249     private final PulseExpansionHandler mPulseExpansionHandler;
250     private final KeyguardBypassController mKeyguardBypassController;
251     private final KeyguardUpdateMonitor mUpdateMonitor;
252     private final ConversationNotificationManager mConversationNotificationManager;
253     private final MediaHierarchyManager mMediaHierarchyManager;
254     private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
255 
256     private KeyguardAffordanceHelper mAffordanceHelper;
257     private KeyguardUserSwitcher mKeyguardUserSwitcher;
258     private KeyguardStatusBarView mKeyguardStatusBar;
259     private ViewGroup mBigClockContainer;
260     private QS mQs;
261     private FrameLayout mQsFrame;
262     private KeyguardStatusView mKeyguardStatusView;
263     private View mQsNavbarScrim;
264     private NotificationsQuickSettingsContainer mNotificationContainerParent;
265     private NotificationStackScrollLayout mNotificationStackScroller;
266     private boolean mAnimateNextPositionUpdate;
267 
268     private int mTrackingPointer;
269     private VelocityTracker mQsVelocityTracker;
270     private boolean mQsTracking;
271 
272     /**
273      * If set, the ongoing touch gesture might both trigger the expansion in {@link PanelView} and
274      * the expansion for quick settings.
275      */
276     private boolean mConflictingQsExpansionGesture;
277 
278     private boolean mPanelExpanded;
279     private boolean mQsExpanded;
280     private boolean mQsExpandedWhenExpandingStarted;
281     private boolean mQsFullyExpanded;
282     private boolean mKeyguardShowing;
283     private boolean mDozing;
284     private boolean mDozingOnDown;
285     private int mBarState;
286     private float mInitialHeightOnTouch;
287     private float mInitialTouchX;
288     private float mInitialTouchY;
289     private float mQsExpansionHeight;
290     private int mQsMinExpansionHeight;
291     private int mQsMaxExpansionHeight;
292     private int mQsPeekHeight;
293     private boolean mStackScrollerOverscrolling;
294     private boolean mQsExpansionFromOverscroll;
295     private float mLastOverscroll;
296     private boolean mQsExpansionEnabled = true;
297     private ValueAnimator mQsExpansionAnimator;
298     private FlingAnimationUtils mFlingAnimationUtils;
299     private int mStatusBarMinHeight;
300     private int mNotificationsHeaderCollideDistance;
301     private float mEmptyDragAmount;
302     private float mDownX;
303     private float mDownY;
304 
305     private final KeyguardClockPositionAlgorithm
306             mClockPositionAlgorithm =
307             new KeyguardClockPositionAlgorithm();
308     private final KeyguardClockPositionAlgorithm.Result
309             mClockPositionResult =
310             new KeyguardClockPositionAlgorithm.Result();
311     private boolean mIsExpanding;
312 
313     private boolean mBlockTouches;
314     // Used for two finger gesture as well as accessibility shortcut to QS.
315     private boolean mQsExpandImmediate;
316     private boolean mTwoFingerQsExpandPossible;
317 
318     /**
319      * If we are in a panel collapsing motion, we reset scrollY of our scroll view but still
320      * need to take this into account in our panel height calculation.
321      */
322     private boolean mQsAnimatorExpand;
323     private boolean mIsLaunchTransitionFinished;
324     private boolean mIsLaunchTransitionRunning;
325     private Runnable mLaunchAnimationEndRunnable;
326     private boolean mOnlyAffordanceInThisMotion;
327     private boolean mKeyguardStatusViewAnimating;
328     private ValueAnimator mQsSizeChangeAnimator;
329 
330     private boolean mShowEmptyShadeView;
331 
332     private boolean mQsScrimEnabled = true;
333     private boolean mQsTouchAboveFalsingThreshold;
334     private int mQsFalsingThreshold;
335 
336     private float mKeyguardStatusBarAnimateAlpha = 1f;
337     private HeadsUpTouchHelper mHeadsUpTouchHelper;
338     private boolean mListenForHeadsUp;
339     private int mNavigationBarBottomHeight;
340     private boolean mExpandingFromHeadsUp;
341     private boolean mCollapsedOnDown;
342     private int mPositionMinSideMargin;
343     private int mLastOrientation = -1;
344     private boolean mClosingWithAlphaFadeOut;
345     private boolean mHeadsUpAnimatingAway;
346     private boolean mLaunchingAffordance;
347     private boolean mAffordanceHasPreview;
348     private FalsingManager mFalsingManager;
349     private String mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_AFFORDANCE;
350 
351     private Runnable mHeadsUpExistenceChangedRunnable = () -> {
352         setHeadsUpAnimatingAway(false);
353         notifyBarPanelExpansionChanged();
354     };
355     private NotificationGroupManager mGroupManager;
356     private boolean mShowIconsWhenExpanded;
357     private int mIndicationBottomPadding;
358     private int mAmbientIndicationBottomPadding;
359     private boolean mIsFullWidth;
360     private boolean mBlockingExpansionForCurrentTouch;
361 
362     /**
363      * Following variables maintain state of events when input focus transfer may occur.
364      */
365     private boolean mExpectingSynthesizedDown; // expecting to see synthesized DOWN event
366     private boolean mLastEventSynthesizedDown; // last event was synthesized DOWN event
367 
368     /**
369      * Current dark amount that follows regular interpolation curve of animation.
370      */
371     private float mInterpolatedDarkAmount;
372 
373     /**
374      * Dark amount that animates from 0 to 1 or vice-versa in linear manner, even if the
375      * interpolation curve is different.
376      */
377     private float mLinearDarkAmount;
378 
379     private boolean mPulsing;
380     private LockscreenGestureLogger mLockscreenGestureLogger = new LockscreenGestureLogger();
381     private boolean mUserSetupComplete;
382     private int mQsNotificationTopPadding;
383     private float mExpandOffset;
384     private boolean mHideIconsDuringNotificationLaunch = true;
385     private int mStackScrollerMeasuringPass;
386     private ArrayList<Consumer<ExpandableNotificationRow>>
387             mTrackingHeadsUpListeners =
388             new ArrayList<>();
389     private ArrayList<Runnable> mVerticalTranslationListener = new ArrayList<>();
390     private HeadsUpAppearanceController mHeadsUpAppearanceController;
391 
392     private int mPanelAlpha;
393     private Runnable mPanelAlphaEndAction;
394     private float mBottomAreaShadeAlpha;
395     private final ValueAnimator mBottomAreaShadeAlphaAnimator;
396     private final AnimatableProperty mPanelAlphaAnimator = AnimatableProperty.from("panelAlpha",
397             NotificationPanelView::setPanelAlphaInternal,
398             NotificationPanelView::getCurrentPanelAlpha,
399             R.id.panel_alpha_animator_tag, R.id.panel_alpha_animator_start_tag,
400             R.id.panel_alpha_animator_end_tag);
401     private final AnimationProperties mPanelAlphaOutPropertiesAnimator =
402             new AnimationProperties().setDuration(150).setCustomInterpolator(
403                     mPanelAlphaAnimator.getProperty(), Interpolators.ALPHA_OUT);
404     private final AnimationProperties mPanelAlphaInPropertiesAnimator =
405             new AnimationProperties().setDuration(200).setAnimationEndAction((property) -> {
406                             if (mPanelAlphaEndAction != null) {
407                                 mPanelAlphaEndAction.run();
408                             }
409                         }).setCustomInterpolator(
410                     mPanelAlphaAnimator.getProperty(), Interpolators.ALPHA_IN);
411     private final NotificationEntryManager mEntryManager;
412 
413     private final CommandQueue mCommandQueue;
414     private final NotificationLockscreenUserManager mLockscreenUserManager;
415     private final ShadeController mShadeController;
416     private int mDisplayId;
417 
418     /**
419      * Cache the resource id of the theme to avoid unnecessary work in onThemeChanged.
420      *
421      * onThemeChanged is forced when the theme might not have changed. So, to avoid unncessary
422      * work, check the current id with the cached id.
423      */
424     private int mThemeResId;
425     private KeyguardIndicationController mKeyguardIndicationController;
426     private Consumer<Boolean> mAffordanceLaunchListener;
427     private int mShelfHeight;
428     private Runnable mOnReinflationListener;
429     private int mDarkIconSize;
430     private int mHeadsUpInset;
431     private boolean mHeadsUpPinnedMode;
432     private float mKeyguardHeadsUpShowingAmount = 0.0f;
433     private boolean mShowingKeyguardHeadsUp;
434     private boolean mAllowExpandForSmallExpansion;
435     private Runnable mExpandAfterLayoutRunnable;
436 
437     /**
438      * Is this a collapse that started on the panel where we should allow the panel to intercept
439      */
440     private boolean mIsPanelCollapseOnQQS;
441 
442     /**
443      * If face auth with bypass is running for the first time after you turn on the screen.
444      * (From aod or screen off)
445      */
446     private boolean mFirstBypassAttempt;
447     /**
448      * If auth happens successfully during {@code mFirstBypassAttempt}, and we should wait until
449      * the keyguard is dismissed to show the status bar.
450      */
451     private boolean mDelayShowingKeyguardStatusBar;
452 
453     private boolean mAnimatingQS;
454     private int mOldLayoutDirection;
455 
456     private View.AccessibilityDelegate mAccessibilityDelegate = new View.AccessibilityDelegate() {
457         @Override
458         public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
459             super.onInitializeAccessibilityNodeInfo(host, info);
460             info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD);
461             info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_UP);
462         }
463 
464         @Override
465         public boolean performAccessibilityAction(View host, int action, Bundle args) {
466             if (action == AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD.getId()
467                     || action
468                     == AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_UP.getId()) {
469                 mStatusBarKeyguardViewManager.showBouncer(true);
470                 return true;
471             }
472             return super.performAccessibilityAction(host, action, args);
473         }
474     };
475 
476     @Inject
NotificationPanelViewController(NotificationPanelView view, InjectionInflationController injectionInflationController, NotificationWakeUpCoordinator coordinator, PulseExpansionHandler pulseExpansionHandler, DynamicPrivacyController dynamicPrivacyController, KeyguardBypassController bypassController, FalsingManager falsingManager, ShadeController shadeController, NotificationLockscreenUserManager notificationLockscreenUserManager, NotificationEntryManager notificationEntryManager, KeyguardStateController keyguardStateController, StatusBarStateController statusBarStateController, DozeLog dozeLog, DozeParameters dozeParameters, CommandQueue commandQueue, VibratorHelper vibratorHelper, LatencyTracker latencyTracker, PowerManager powerManager, AccessibilityManager accessibilityManager, @DisplayId int displayId, KeyguardUpdateMonitor keyguardUpdateMonitor, MetricsLogger metricsLogger, ActivityManager activityManager, ZenModeController zenModeController, ConfigurationController configurationController, FlingAnimationUtils.Builder flingAnimationUtilsBuilder, StatusBarTouchableRegionManager statusBarTouchableRegionManager, ConversationNotificationManager conversationNotificationManager, MediaHierarchyManager mediaHierarchyManager, BiometricUnlockController biometricUnlockController, StatusBarKeyguardViewManager statusBarKeyguardViewManager)477     public NotificationPanelViewController(NotificationPanelView view,
478             InjectionInflationController injectionInflationController,
479             NotificationWakeUpCoordinator coordinator, PulseExpansionHandler pulseExpansionHandler,
480             DynamicPrivacyController dynamicPrivacyController,
481             KeyguardBypassController bypassController, FalsingManager falsingManager,
482             ShadeController shadeController,
483             NotificationLockscreenUserManager notificationLockscreenUserManager,
484             NotificationEntryManager notificationEntryManager,
485             KeyguardStateController keyguardStateController,
486             StatusBarStateController statusBarStateController, DozeLog dozeLog,
487             DozeParameters dozeParameters, CommandQueue commandQueue, VibratorHelper vibratorHelper,
488             LatencyTracker latencyTracker, PowerManager powerManager,
489             AccessibilityManager accessibilityManager, @DisplayId int displayId,
490             KeyguardUpdateMonitor keyguardUpdateMonitor, MetricsLogger metricsLogger,
491             ActivityManager activityManager, ZenModeController zenModeController,
492             ConfigurationController configurationController,
493             FlingAnimationUtils.Builder flingAnimationUtilsBuilder,
494             StatusBarTouchableRegionManager statusBarTouchableRegionManager,
495             ConversationNotificationManager conversationNotificationManager,
496             MediaHierarchyManager mediaHierarchyManager,
497             BiometricUnlockController biometricUnlockController,
498             StatusBarKeyguardViewManager statusBarKeyguardViewManager) {
499         super(view, falsingManager, dozeLog, keyguardStateController,
500                 (SysuiStatusBarStateController) statusBarStateController, vibratorHelper,
501                 latencyTracker, flingAnimationUtilsBuilder, statusBarTouchableRegionManager);
502         mView = view;
503         mMetricsLogger = metricsLogger;
504         mActivityManager = activityManager;
505         mZenModeController = zenModeController;
506         mConfigurationController = configurationController;
507         mFlingAnimationUtilsBuilder = flingAnimationUtilsBuilder;
508         mMediaHierarchyManager = mediaHierarchyManager;
509         mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
510         mView.setWillNotDraw(!DEBUG);
511         mInjectionInflationController = injectionInflationController;
512         mFalsingManager = falsingManager;
513         mPowerManager = powerManager;
514         mWakeUpCoordinator = coordinator;
515         mAccessibilityManager = accessibilityManager;
516         mView.setAccessibilityPaneTitle(determineAccessibilityPaneTitle());
517         setPanelAlpha(255, false /* animate */);
518         mCommandQueue = commandQueue;
519         mDisplayId = displayId;
520         mPulseExpansionHandler = pulseExpansionHandler;
521         mDozeParameters = dozeParameters;
522         mBiometricUnlockController = biometricUnlockController;
523         pulseExpansionHandler.setPulseExpandAbortListener(() -> {
524             if (mQs != null) {
525                 mQs.animateHeaderSlidingOut();
526             }
527         });
528         mThemeResId = mView.getContext().getThemeResId();
529         mKeyguardBypassController = bypassController;
530         mUpdateMonitor = keyguardUpdateMonitor;
531         mFirstBypassAttempt = mKeyguardBypassController.getBypassEnabled();
532         KeyguardStateController.Callback
533                 keyguardMonitorCallback =
534                 new KeyguardStateController.Callback() {
535                     @Override
536                     public void onKeyguardFadingAwayChanged() {
537                         if (!mKeyguardStateController.isKeyguardFadingAway()) {
538                             mFirstBypassAttempt = false;
539                             mDelayShowingKeyguardStatusBar = false;
540                         }
541                     }
542                 };
543         mKeyguardStateController.addCallback(keyguardMonitorCallback);
544         DynamicPrivacyControlListener
545                 dynamicPrivacyControlListener =
546                 new DynamicPrivacyControlListener();
547         dynamicPrivacyController.addListener(dynamicPrivacyControlListener);
548 
549         mBottomAreaShadeAlphaAnimator = ValueAnimator.ofFloat(1f, 0);
550         mBottomAreaShadeAlphaAnimator.addUpdateListener(animation -> {
551             mBottomAreaShadeAlpha = (float) animation.getAnimatedValue();
552             updateKeyguardBottomAreaAlpha();
553         });
554         mBottomAreaShadeAlphaAnimator.setDuration(160);
555         mBottomAreaShadeAlphaAnimator.setInterpolator(Interpolators.ALPHA_OUT);
556         mShadeController = shadeController;
557         mLockscreenUserManager = notificationLockscreenUserManager;
558         mEntryManager = notificationEntryManager;
559         mConversationNotificationManager = conversationNotificationManager;
560 
561         mView.setBackgroundColor(Color.TRANSPARENT);
562         OnAttachStateChangeListener onAttachStateChangeListener = new OnAttachStateChangeListener();
563         mView.addOnAttachStateChangeListener(onAttachStateChangeListener);
564         if (mView.isAttachedToWindow()) {
565             onAttachStateChangeListener.onViewAttachedToWindow(mView);
566         }
567 
568         mView.setOnApplyWindowInsetsListener(new OnApplyWindowInsetsListener());
569 
570         if (DEBUG) {
571             mView.getOverlay().add(new DebugDrawable());
572         }
573 
574         onFinishInflate();
575     }
576 
onFinishInflate()577     private void onFinishInflate() {
578         loadDimens();
579         mKeyguardStatusBar = mView.findViewById(R.id.keyguard_header);
580         mKeyguardStatusView = mView.findViewById(R.id.keyguard_status_view);
581 
582         KeyguardClockSwitch keyguardClockSwitch = mView.findViewById(R.id.keyguard_clock_container);
583         mBigClockContainer = mView.findViewById(R.id.big_clock_container);
584         keyguardClockSwitch.setBigClockContainer(mBigClockContainer);
585 
586         mNotificationContainerParent = mView.findViewById(R.id.notification_container_parent);
587         mNotificationStackScroller = mView.findViewById(R.id.notification_stack_scroller);
588         mNotificationStackScroller.setOnHeightChangedListener(mOnHeightChangedListener);
589         mNotificationStackScroller.setOverscrollTopChangedListener(mOnOverscrollTopChangedListener);
590         mNotificationStackScroller.setOnEmptySpaceClickListener(mOnEmptySpaceClickListener);
591         addTrackingHeadsUpListener(mNotificationStackScroller::setTrackingHeadsUp);
592         mKeyguardBottomArea = mView.findViewById(R.id.keyguard_bottom_area);
593         mQsNavbarScrim = mView.findViewById(R.id.qs_navbar_scrim);
594         mLastOrientation = mResources.getConfiguration().orientation;
595 
596         initBottomArea();
597 
598         mWakeUpCoordinator.setStackScroller(mNotificationStackScroller);
599         mQsFrame = mView.findViewById(R.id.qs_frame);
600         mPulseExpansionHandler.setUp(
601                 mNotificationStackScroller, mExpansionCallback, mShadeController);
602         mWakeUpCoordinator.addListener(new NotificationWakeUpCoordinator.WakeUpListener() {
603             @Override
604             public void onFullyHiddenChanged(boolean isFullyHidden) {
605                 updateKeyguardStatusBarForHeadsUp();
606             }
607 
608             @Override
609             public void onPulseExpansionChanged(boolean expandingChanged) {
610                 if (mKeyguardBypassController.getBypassEnabled()) {
611                     // Position the notifications while dragging down while pulsing
612                     requestScrollerTopPaddingUpdate(false /* animate */);
613                     updateQSPulseExpansion();
614                 }
615             }
616         });
617 
618         mView.setRtlChangeListener(layoutDirection -> {
619             if (layoutDirection != mOldLayoutDirection) {
620                 mAffordanceHelper.onRtlPropertiesChanged();
621                 mOldLayoutDirection = layoutDirection;
622             }
623         });
624 
625         mView.setAccessibilityDelegate(mAccessibilityDelegate);
626     }
627 
628     @Override
loadDimens()629     protected void loadDimens() {
630         super.loadDimens();
631         mFlingAnimationUtils = mFlingAnimationUtilsBuilder.reset()
632                 .setMaxLengthSeconds(0.4f).build();
633         mStatusBarMinHeight = mResources.getDimensionPixelSize(
634                 com.android.internal.R.dimen.status_bar_height);
635         mQsPeekHeight = mResources.getDimensionPixelSize(R.dimen.qs_peek_height);
636         mNotificationsHeaderCollideDistance = mResources.getDimensionPixelSize(
637                 R.dimen.header_notifications_collide_distance);
638         mClockPositionAlgorithm.loadDimens(mResources);
639         mQsFalsingThreshold = mResources.getDimensionPixelSize(R.dimen.qs_falsing_threshold);
640         mPositionMinSideMargin = mResources.getDimensionPixelSize(
641                 R.dimen.notification_panel_min_side_margin);
642         mIndicationBottomPadding = mResources.getDimensionPixelSize(
643                 R.dimen.keyguard_indication_bottom_padding);
644         mQsNotificationTopPadding = mResources.getDimensionPixelSize(
645                 R.dimen.qs_notification_padding);
646         mShelfHeight = mResources.getDimensionPixelSize(R.dimen.notification_shelf_height);
647         mDarkIconSize = mResources.getDimensionPixelSize(R.dimen.status_bar_icon_drawing_size_dark);
648         int statusbarHeight = mResources.getDimensionPixelSize(
649                 com.android.internal.R.dimen.status_bar_height);
650         mHeadsUpInset = statusbarHeight + mResources.getDimensionPixelSize(
651                 R.dimen.heads_up_status_bar_padding);
652     }
653 
654     /**
655      * Returns if there's a custom clock being presented.
656      */
hasCustomClock()657     public boolean hasCustomClock() {
658         return mKeyguardStatusView.hasCustomClock();
659     }
660 
setStatusBar(StatusBar bar)661     private void setStatusBar(StatusBar bar) {
662         // TODO: this can be injected.
663         mStatusBar = bar;
664         mKeyguardBottomArea.setStatusBar(mStatusBar);
665     }
666     /**
667      * @see #launchCamera(boolean, int)
668      * @see #setLaunchingAffordance(boolean)
669      */
setLaunchAffordanceListener(Consumer<Boolean> listener)670     public void setLaunchAffordanceListener(Consumer<Boolean> listener) {
671         mAffordanceLaunchListener = listener;
672     }
673 
updateResources()674     public void updateResources() {
675         int qsWidth = mResources.getDimensionPixelSize(R.dimen.qs_panel_width);
676         int panelGravity = mResources.getInteger(R.integer.notification_panel_layout_gravity);
677         FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mQsFrame.getLayoutParams();
678         if (lp.width != qsWidth || lp.gravity != panelGravity) {
679             lp.width = qsWidth;
680             lp.gravity = panelGravity;
681             mQsFrame.setLayoutParams(lp);
682         }
683 
684         int panelWidth = mResources.getDimensionPixelSize(R.dimen.notification_panel_width);
685         lp = (FrameLayout.LayoutParams) mNotificationStackScroller.getLayoutParams();
686         if (lp.width != panelWidth || lp.gravity != panelGravity) {
687             lp.width = panelWidth;
688             lp.gravity = panelGravity;
689             mNotificationStackScroller.setLayoutParams(lp);
690         }
691     }
692 
reInflateViews()693     private void reInflateViews() {
694         updateShowEmptyShadeView();
695 
696         // Re-inflate the status view group.
697         int index = mView.indexOfChild(mKeyguardStatusView);
698         mView.removeView(mKeyguardStatusView);
699         mKeyguardStatusView = (KeyguardStatusView) mInjectionInflationController.injectable(
700                 LayoutInflater.from(mView.getContext())).inflate(
701                 R.layout.keyguard_status_view, mView, false);
702         mView.addView(mKeyguardStatusView, index);
703 
704         // Re-associate the clock container with the keyguard clock switch.
705         mBigClockContainer.removeAllViews();
706         KeyguardClockSwitch keyguardClockSwitch = mView.findViewById(R.id.keyguard_clock_container);
707         keyguardClockSwitch.setBigClockContainer(mBigClockContainer);
708 
709         // Update keyguard bottom area
710         index = mView.indexOfChild(mKeyguardBottomArea);
711         mView.removeView(mKeyguardBottomArea);
712         KeyguardBottomAreaView oldBottomArea = mKeyguardBottomArea;
713         mKeyguardBottomArea = (KeyguardBottomAreaView) mInjectionInflationController.injectable(
714                 LayoutInflater.from(mView.getContext())).inflate(
715                 R.layout.keyguard_bottom_area, mView, false);
716         mKeyguardBottomArea.initFrom(oldBottomArea);
717         mView.addView(mKeyguardBottomArea, index);
718         initBottomArea();
719         mKeyguardIndicationController.setIndicationArea(mKeyguardBottomArea);
720         mStatusBarStateListener.onDozeAmountChanged(mStatusBarStateController.getDozeAmount(),
721                 mStatusBarStateController.getInterpolatedDozeAmount());
722 
723         if (mKeyguardStatusBar != null) {
724             mKeyguardStatusBar.onThemeChanged();
725         }
726 
727         setKeyguardStatusViewVisibility(mBarState, false, false);
728         setKeyguardBottomAreaVisibility(mBarState, false);
729         if (mOnReinflationListener != null) {
730             mOnReinflationListener.run();
731         }
732     }
733 
initBottomArea()734     private void initBottomArea() {
735         mAffordanceHelper = new KeyguardAffordanceHelper(
736                 mKeyguardAffordanceHelperCallback, mView.getContext(), mFalsingManager);
737         mKeyguardBottomArea.setAffordanceHelper(mAffordanceHelper);
738         mKeyguardBottomArea.setStatusBar(mStatusBar);
739         mKeyguardBottomArea.setUserSetupComplete(mUserSetupComplete);
740     }
741 
setKeyguardIndicationController(KeyguardIndicationController indicationController)742     public void setKeyguardIndicationController(KeyguardIndicationController indicationController) {
743         mKeyguardIndicationController = indicationController;
744         mKeyguardIndicationController.setIndicationArea(mKeyguardBottomArea);
745     }
746 
updateGestureExclusionRect()747     private void updateGestureExclusionRect() {
748         Rect exclusionRect = calculateGestureExclusionRect();
749         mView.setSystemGestureExclusionRects(exclusionRect.isEmpty() ? Collections.EMPTY_LIST
750                 : Collections.singletonList(exclusionRect));
751     }
752 
calculateGestureExclusionRect()753     private Rect calculateGestureExclusionRect() {
754         Rect exclusionRect = null;
755         Region touchableRegion = mStatusBarTouchableRegionManager.calculateTouchableRegion();
756         if (isFullyCollapsed() && touchableRegion != null) {
757             // Note: The manager also calculates the non-pinned touchable region
758             exclusionRect = touchableRegion.getBounds();
759         }
760         return exclusionRect != null ? exclusionRect : EMPTY_RECT;
761     }
762 
setIsFullWidth(boolean isFullWidth)763     private void setIsFullWidth(boolean isFullWidth) {
764         mIsFullWidth = isFullWidth;
765         mNotificationStackScroller.setIsFullWidth(isFullWidth);
766     }
767 
startQsSizeChangeAnimation(int oldHeight, final int newHeight)768     private void startQsSizeChangeAnimation(int oldHeight, final int newHeight) {
769         if (mQsSizeChangeAnimator != null) {
770             oldHeight = (int) mQsSizeChangeAnimator.getAnimatedValue();
771             mQsSizeChangeAnimator.cancel();
772         }
773         mQsSizeChangeAnimator = ValueAnimator.ofInt(oldHeight, newHeight);
774         mQsSizeChangeAnimator.setDuration(300);
775         mQsSizeChangeAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
776         mQsSizeChangeAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
777             @Override
778             public void onAnimationUpdate(ValueAnimator animation) {
779                 requestScrollerTopPaddingUpdate(false /* animate */);
780                 requestPanelHeightUpdate();
781                 int height = (int) mQsSizeChangeAnimator.getAnimatedValue();
782                 mQs.setHeightOverride(height);
783             }
784         });
785         mQsSizeChangeAnimator.addListener(new AnimatorListenerAdapter() {
786             @Override
787             public void onAnimationEnd(Animator animation) {
788                 mQsSizeChangeAnimator = null;
789             }
790         });
791         mQsSizeChangeAnimator.start();
792     }
793 
794     /**
795      * Positions the clock and notifications dynamically depending on how many notifications are
796      * showing.
797      */
positionClockAndNotifications()798     private void positionClockAndNotifications() {
799         boolean animate = mNotificationStackScroller.isAddOrRemoveAnimationPending();
800         boolean animateClock = animate || mAnimateNextPositionUpdate;
801         int stackScrollerPadding;
802         if (mBarState != StatusBarState.KEYGUARD) {
803             stackScrollerPadding = getUnlockedStackScrollerPadding();
804         } else {
805             int totalHeight = mView.getHeight();
806             int bottomPadding = Math.max(mIndicationBottomPadding, mAmbientIndicationBottomPadding);
807             int clockPreferredY = mKeyguardStatusView.getClockPreferredY(totalHeight);
808             boolean bypassEnabled = mKeyguardBypassController.getBypassEnabled();
809             final boolean
810                     hasVisibleNotifications =
811                     !bypassEnabled && mNotificationStackScroller.getVisibleNotificationCount() != 0;
812             mKeyguardStatusView.setHasVisibleNotifications(hasVisibleNotifications);
813             mClockPositionAlgorithm.setup(mStatusBarMinHeight, totalHeight - bottomPadding,
814                     mNotificationStackScroller.getIntrinsicContentHeight(), getExpandedFraction(),
815                     totalHeight, (int) (mKeyguardStatusView.getHeight() - mShelfHeight / 2.0f
816                             - mDarkIconSize / 2.0f), clockPreferredY, hasCustomClock(),
817                     hasVisibleNotifications, mInterpolatedDarkAmount, mEmptyDragAmount,
818                     bypassEnabled, getUnlockedStackScrollerPadding());
819             mClockPositionAlgorithm.run(mClockPositionResult);
820             PropertyAnimator.setProperty(mKeyguardStatusView, AnimatableProperty.X,
821                     mClockPositionResult.clockX, CLOCK_ANIMATION_PROPERTIES, animateClock);
822             PropertyAnimator.setProperty(mKeyguardStatusView, AnimatableProperty.Y,
823                     mClockPositionResult.clockY, CLOCK_ANIMATION_PROPERTIES, animateClock);
824             updateNotificationTranslucency();
825             updateClock();
826             stackScrollerPadding = mClockPositionResult.stackScrollerPaddingExpanded;
827         }
828         mNotificationStackScroller.setIntrinsicPadding(stackScrollerPadding);
829         mKeyguardBottomArea.setAntiBurnInOffsetX(mClockPositionResult.clockX);
830 
831         mStackScrollerMeasuringPass++;
832         requestScrollerTopPaddingUpdate(animate);
833         mStackScrollerMeasuringPass = 0;
834         mAnimateNextPositionUpdate = false;
835     }
836 
837     /**
838      * @return the padding of the stackscroller when unlocked
839      */
getUnlockedStackScrollerPadding()840     private int getUnlockedStackScrollerPadding() {
841         return (mQs != null ? mQs.getHeader().getHeight() : 0) + mQsPeekHeight
842                 + mQsNotificationTopPadding;
843     }
844 
845     /**
846      * @param maximum the maximum to return at most
847      * @return the maximum keyguard notifications that can fit on the screen
848      */
computeMaxKeyguardNotifications(int maximum)849     public int computeMaxKeyguardNotifications(int maximum) {
850         float minPadding = mClockPositionAlgorithm.getMinStackScrollerPadding();
851         int notificationPadding = Math.max(
852                 1, mResources.getDimensionPixelSize(R.dimen.notification_divider_height));
853         NotificationShelf shelf = mNotificationStackScroller.getNotificationShelf();
854         float
855                 shelfSize =
856                 shelf.getVisibility() == View.GONE ? 0
857                         : shelf.getIntrinsicHeight() + notificationPadding;
858         float
859                 availableSpace =
860                 mNotificationStackScroller.getHeight() - minPadding - shelfSize - Math.max(
861                         mIndicationBottomPadding, mAmbientIndicationBottomPadding)
862                         - mKeyguardStatusView.getLogoutButtonHeight();
863         int count = 0;
864         ExpandableView previousView = null;
865         for (int i = 0; i < mNotificationStackScroller.getChildCount(); i++) {
866             ExpandableView child = (ExpandableView) mNotificationStackScroller.getChildAt(i);
867             if (!canShowViewOnLockscreen(child)) {
868                 continue;
869             }
870             availableSpace -= child.getMinHeight(true /* ignoreTemporaryStates */);
871             availableSpace -= count == 0 ? 0 : notificationPadding;
872             availableSpace -= mNotificationStackScroller.calculateGapHeight(previousView, child,
873                     count);
874             previousView = child;
875             if (availableSpace >= 0 && count < maximum) {
876                 count++;
877             } else if (availableSpace > -shelfSize) {
878                 // if we are exactly the last view, then we can show us still!
879                 for (int j = i + 1; j < mNotificationStackScroller.getChildCount(); j++) {
880                     ExpandableView view = (ExpandableView) mNotificationStackScroller.getChildAt(j);
881                     if (view instanceof ExpandableNotificationRow &&
882                             canShowViewOnLockscreen(view)) {
883                         return count;
884                     }
885                 }
886                 count++;
887                 return count;
888             } else {
889                 return count;
890             }
891         }
892         return count;
893     }
894 
895     /**
896      * Can a view be shown on the lockscreen when calculating the number of allowed notifications
897      * to show?
898      *
899      * @param child the view in question
900      * @return true if it can be shown
901      */
canShowViewOnLockscreen(ExpandableView child)902     private boolean canShowViewOnLockscreen(ExpandableView child) {
903         if (child.hasNoContentHeight()) {
904             return false;
905         }
906         if (child instanceof ExpandableNotificationRow &&
907                 !canShowRowOnLockscreen((ExpandableNotificationRow) child)) {
908             return false;
909         } else if (child.getVisibility() == GONE) {
910             // ENRs can be gone and count because their visibility is only set after
911             // this calculation, but all other views should be up to date
912             return false;
913         }
914         return true;
915     }
916 
917     /**
918      * Can a row be shown on the lockscreen when calculating the number of allowed notifications
919      * to show?
920      *
921      * @param row the row in question
922      * @return true if it can be shown
923      */
canShowRowOnLockscreen(ExpandableNotificationRow row)924     private boolean canShowRowOnLockscreen(ExpandableNotificationRow row) {
925         boolean suppressedSummary =
926                 mGroupManager != null && mGroupManager.isSummaryOfSuppressedGroup(
927                         row.getEntry().getSbn());
928         if (suppressedSummary) {
929             return false;
930         }
931         if (!mLockscreenUserManager.shouldShowOnKeyguard(row.getEntry())) {
932             return false;
933         }
934         if (row.isRemoved()) {
935             return false;
936         }
937         return true;
938     }
939 
updateClock()940     private void updateClock() {
941         if (!mKeyguardStatusViewAnimating) {
942             mKeyguardStatusView.setAlpha(mClockPositionResult.clockAlpha);
943         }
944     }
945 
animateToFullShade(long delay)946     public void animateToFullShade(long delay) {
947         mNotificationStackScroller.goToFullShade(delay);
948         mView.requestLayout();
949         mAnimateNextPositionUpdate = true;
950     }
951 
setQsExpansionEnabled(boolean qsExpansionEnabled)952     public void setQsExpansionEnabled(boolean qsExpansionEnabled) {
953         mQsExpansionEnabled = qsExpansionEnabled;
954         if (mQs == null) return;
955         mQs.setHeaderClickable(qsExpansionEnabled);
956     }
957 
958     @Override
resetViews(boolean animate)959     public void resetViews(boolean animate) {
960         mIsLaunchTransitionFinished = false;
961         mBlockTouches = false;
962         if (!mLaunchingAffordance) {
963             mAffordanceHelper.reset(false);
964             mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_AFFORDANCE;
965         }
966         mStatusBar.getGutsManager().closeAndSaveGuts(true /* leavebehind */, true /* force */,
967                 true /* controls */, -1 /* x */, -1 /* y */, true /* resetMenu */);
968         if (animate) {
969             animateCloseQs(true /* animateAway */);
970         } else {
971             closeQs();
972         }
973         mNotificationStackScroller.setOverScrollAmount(0f, true /* onTop */, animate,
974                 !animate /* cancelAnimators */);
975         mNotificationStackScroller.resetScrollPosition();
976     }
977 
978     @Override
collapse(boolean delayed, float speedUpFactor)979     public void collapse(boolean delayed, float speedUpFactor) {
980         if (!canPanelBeCollapsed()) {
981             return;
982         }
983 
984         if (mQsExpanded) {
985             mQsExpandImmediate = true;
986             mNotificationStackScroller.setShouldShowShelfOnly(true);
987         }
988         super.collapse(delayed, speedUpFactor);
989     }
990 
closeQs()991     public void closeQs() {
992         cancelQsAnimation();
993         setQsExpansion(mQsMinExpansionHeight);
994     }
995 
cancelAnimation()996     public void cancelAnimation() {
997         mView.animate().cancel();
998     }
999 
1000 
1001     /**
1002      * Animate QS closing by flinging it.
1003      * If QS is expanded, it will collapse into QQS and stop.
1004      *
1005      * @param animateAway Do not stop when QS becomes QQS. Fling until QS isn't visible anymore.
1006      */
animateCloseQs(boolean animateAway)1007     public void animateCloseQs(boolean animateAway) {
1008         if (mQsExpansionAnimator != null) {
1009             if (!mQsAnimatorExpand) {
1010                 return;
1011             }
1012             float height = mQsExpansionHeight;
1013             mQsExpansionAnimator.cancel();
1014             setQsExpansion(height);
1015         }
1016         flingSettings(0 /* vel */, animateAway ? FLING_HIDE : FLING_COLLAPSE);
1017     }
1018 
expandWithQs()1019     public void expandWithQs() {
1020         if (mQsExpansionEnabled) {
1021             mQsExpandImmediate = true;
1022             mNotificationStackScroller.setShouldShowShelfOnly(true);
1023         }
1024         if (isFullyCollapsed()) {
1025             expand(true /* animate */);
1026         } else {
1027             flingSettings(0 /* velocity */, FLING_EXPAND);
1028         }
1029     }
1030 
expandWithoutQs()1031     public void expandWithoutQs() {
1032         if (isQsExpanded()) {
1033             flingSettings(0 /* velocity */, FLING_COLLAPSE);
1034         } else {
1035             expand(true /* animate */);
1036         }
1037     }
1038 
1039     @Override
fling(float vel, boolean expand)1040     public void fling(float vel, boolean expand) {
1041         GestureRecorder gr = ((PhoneStatusBarView) mBar).mBar.getGestureRecorder();
1042         if (gr != null) {
1043             gr.tag("fling " + ((vel > 0) ? "open" : "closed"), "notifications,v=" + vel);
1044         }
1045         super.fling(vel, expand);
1046     }
1047 
1048     @Override
flingToHeight(float vel, boolean expand, float target, float collapseSpeedUpFactor, boolean expandBecauseOfFalsing)1049     protected void flingToHeight(float vel, boolean expand, float target,
1050             float collapseSpeedUpFactor, boolean expandBecauseOfFalsing) {
1051         mHeadsUpTouchHelper.notifyFling(!expand);
1052         setClosingWithAlphaFadeout(!expand && !isOnKeyguard() && getFadeoutAlpha() == 1.0f);
1053         super.flingToHeight(vel, expand, target, collapseSpeedUpFactor, expandBecauseOfFalsing);
1054     }
1055 
1056 
onQsIntercept(MotionEvent event)1057     private boolean onQsIntercept(MotionEvent event) {
1058         int pointerIndex = event.findPointerIndex(mTrackingPointer);
1059         if (pointerIndex < 0) {
1060             pointerIndex = 0;
1061             mTrackingPointer = event.getPointerId(pointerIndex);
1062         }
1063         final float x = event.getX(pointerIndex);
1064         final float y = event.getY(pointerIndex);
1065 
1066         switch (event.getActionMasked()) {
1067             case MotionEvent.ACTION_DOWN:
1068                 mInitialTouchY = y;
1069                 mInitialTouchX = x;
1070                 initVelocityTracker();
1071                 trackMovement(event);
1072                 if (mKeyguardShowing
1073                         && shouldQuickSettingsIntercept(mInitialTouchX, mInitialTouchY, 0)) {
1074                     // Dragging down on the lockscreen statusbar should prohibit other interactions
1075                     // immediately, otherwise we'll wait on the touchslop. This is to allow
1076                     // dragging down to expanded quick settings directly on the lockscreen.
1077                     mView.getParent().requestDisallowInterceptTouchEvent(true);
1078                 }
1079                 if (mQsExpansionAnimator != null) {
1080                     onQsExpansionStarted();
1081                     mInitialHeightOnTouch = mQsExpansionHeight;
1082                     mQsTracking = true;
1083                     mNotificationStackScroller.cancelLongPress();
1084                 }
1085                 break;
1086             case MotionEvent.ACTION_POINTER_UP:
1087                 final int upPointer = event.getPointerId(event.getActionIndex());
1088                 if (mTrackingPointer == upPointer) {
1089                     // gesture is ongoing, find a new pointer to track
1090                     final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1;
1091                     mTrackingPointer = event.getPointerId(newIndex);
1092                     mInitialTouchX = event.getX(newIndex);
1093                     mInitialTouchY = event.getY(newIndex);
1094                 }
1095                 break;
1096 
1097             case MotionEvent.ACTION_MOVE:
1098                 final float h = y - mInitialTouchY;
1099                 trackMovement(event);
1100                 if (mQsTracking) {
1101 
1102                     // Already tracking because onOverscrolled was called. We need to update here
1103                     // so we don't stop for a frame until the next touch event gets handled in
1104                     // onTouchEvent.
1105                     setQsExpansion(h + mInitialHeightOnTouch);
1106                     trackMovement(event);
1107                     return true;
1108                 }
1109                 if ((h > getTouchSlop(event) || (h < -getTouchSlop(event) && mQsExpanded))
1110                         && Math.abs(h) > Math.abs(x - mInitialTouchX)
1111                         && shouldQuickSettingsIntercept(mInitialTouchX, mInitialTouchY, h)) {
1112                     mView.getParent().requestDisallowInterceptTouchEvent(true);
1113                     mQsTracking = true;
1114                     onQsExpansionStarted();
1115                     notifyExpandingFinished();
1116                     mInitialHeightOnTouch = mQsExpansionHeight;
1117                     mInitialTouchY = y;
1118                     mInitialTouchX = x;
1119                     mNotificationStackScroller.cancelLongPress();
1120                     return true;
1121                 }
1122                 break;
1123 
1124             case MotionEvent.ACTION_CANCEL:
1125             case MotionEvent.ACTION_UP:
1126                 trackMovement(event);
1127                 if (mQsTracking) {
1128                     flingQsWithCurrentVelocity(y,
1129                             event.getActionMasked() == MotionEvent.ACTION_CANCEL);
1130                     mQsTracking = false;
1131                 }
1132                 break;
1133         }
1134         return false;
1135     }
1136 
1137     @Override
isInContentBounds(float x, float y)1138     protected boolean isInContentBounds(float x, float y) {
1139         float stackScrollerX = mNotificationStackScroller.getX();
1140         return !mNotificationStackScroller.isBelowLastNotification(x - stackScrollerX, y)
1141                 && stackScrollerX < x && x < stackScrollerX + mNotificationStackScroller.getWidth();
1142     }
1143 
initDownStates(MotionEvent event)1144     private void initDownStates(MotionEvent event) {
1145         if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
1146             mOnlyAffordanceInThisMotion = false;
1147             mQsTouchAboveFalsingThreshold = mQsFullyExpanded;
1148             mDozingOnDown = isDozing();
1149             mDownX = event.getX();
1150             mDownY = event.getY();
1151             mCollapsedOnDown = isFullyCollapsed();
1152             mIsPanelCollapseOnQQS = canPanelCollapseOnQQS(mDownX, mDownY);
1153             mListenForHeadsUp = mCollapsedOnDown && mHeadsUpManager.hasPinnedHeadsUp();
1154             mAllowExpandForSmallExpansion = mExpectingSynthesizedDown;
1155             mTouchSlopExceededBeforeDown = mExpectingSynthesizedDown;
1156             if (mExpectingSynthesizedDown) {
1157                 mLastEventSynthesizedDown = true;
1158             } else {
1159                 // down but not synthesized motion event.
1160                 mLastEventSynthesizedDown = false;
1161             }
1162         } else {
1163             // not down event at all.
1164             mLastEventSynthesizedDown = false;
1165         }
1166     }
1167 
1168     /**
1169      * Can the panel collapse in this motion because it was started on QQS?
1170      *
1171      * @param downX the x location where the touch started
1172      * @param downY the y location where the touch started
1173      *
1174      * @return true if the panel could be collapsed because it stared on QQS
1175      */
canPanelCollapseOnQQS(float downX, float downY)1176     private boolean canPanelCollapseOnQQS(float downX, float downY) {
1177         if (mCollapsedOnDown || mKeyguardShowing || mQsExpanded) {
1178             return false;
1179         }
1180         View header = mQs == null ? mKeyguardStatusBar : mQs.getHeader();
1181         return downX >= mQsFrame.getX() && downX <= mQsFrame.getX() + mQsFrame.getWidth()
1182                         && downY <= header.getBottom();
1183 
1184     }
1185 
flingQsWithCurrentVelocity(float y, boolean isCancelMotionEvent)1186     private void flingQsWithCurrentVelocity(float y, boolean isCancelMotionEvent) {
1187         float vel = getCurrentQSVelocity();
1188         final boolean expandsQs = flingExpandsQs(vel);
1189         if (expandsQs) {
1190             logQsSwipeDown(y);
1191         }
1192         flingSettings(vel, expandsQs && !isCancelMotionEvent ? FLING_EXPAND : FLING_COLLAPSE);
1193     }
1194 
logQsSwipeDown(float y)1195     private void logQsSwipeDown(float y) {
1196         float vel = getCurrentQSVelocity();
1197         final int
1198                 gesture =
1199                 mBarState == StatusBarState.KEYGUARD ? MetricsEvent.ACTION_LS_QS
1200                         : MetricsEvent.ACTION_SHADE_QS_PULL;
1201         mLockscreenGestureLogger.write(gesture,
1202                 (int) ((y - mInitialTouchY) / mStatusBar.getDisplayDensity()),
1203                 (int) (vel / mStatusBar.getDisplayDensity()));
1204     }
1205 
flingExpandsQs(float vel)1206     private boolean flingExpandsQs(float vel) {
1207         if (mFalsingManager.isUnlockingDisabled() || isFalseTouch()) {
1208             return false;
1209         }
1210         if (Math.abs(vel) < mFlingAnimationUtils.getMinVelocityPxPerSecond()) {
1211             return getQsExpansionFraction() > 0.5f;
1212         } else {
1213             return vel > 0;
1214         }
1215     }
1216 
isFalseTouch()1217     private boolean isFalseTouch() {
1218         if (!mKeyguardAffordanceHelperCallback.needsAntiFalsing()) {
1219             return false;
1220         }
1221         if (mFalsingManager.isClassifierEnabled()) {
1222             return mFalsingManager.isFalseTouch();
1223         }
1224         return !mQsTouchAboveFalsingThreshold;
1225     }
1226 
getQsExpansionFraction()1227     private float getQsExpansionFraction() {
1228         return Math.min(
1229                 1f, (mQsExpansionHeight - mQsMinExpansionHeight) / (mQsMaxExpansionHeight
1230                         - mQsMinExpansionHeight));
1231     }
1232 
1233     @Override
shouldExpandWhenNotFlinging()1234     protected boolean shouldExpandWhenNotFlinging() {
1235         if (super.shouldExpandWhenNotFlinging()) {
1236             return true;
1237         }
1238         if (mAllowExpandForSmallExpansion) {
1239             // When we get a touch that came over from launcher, the velocity isn't always correct
1240             // Let's err on expanding if the gesture has been reasonably slow
1241             long timeSinceDown = SystemClock.uptimeMillis() - mDownTime;
1242             return timeSinceDown <= MAX_TIME_TO_OPEN_WHEN_FLINGING_FROM_LAUNCHER;
1243         }
1244         return false;
1245     }
1246 
1247     @Override
getOpeningHeight()1248     protected float getOpeningHeight() {
1249         return mNotificationStackScroller.getOpeningHeight();
1250     }
1251 
1252 
handleQsTouch(MotionEvent event)1253     private boolean handleQsTouch(MotionEvent event) {
1254         final int action = event.getActionMasked();
1255         if (action == MotionEvent.ACTION_DOWN && getExpandedFraction() == 1f
1256                 && mBarState != StatusBarState.KEYGUARD && !mQsExpanded && mQsExpansionEnabled) {
1257 
1258             // Down in the empty area while fully expanded - go to QS.
1259             mQsTracking = true;
1260             mConflictingQsExpansionGesture = true;
1261             onQsExpansionStarted();
1262             mInitialHeightOnTouch = mQsExpansionHeight;
1263             mInitialTouchY = event.getX();
1264             mInitialTouchX = event.getY();
1265         }
1266         if (!isFullyCollapsed()) {
1267             handleQsDown(event);
1268         }
1269         if (!mQsExpandImmediate && mQsTracking) {
1270             onQsTouch(event);
1271             if (!mConflictingQsExpansionGesture) {
1272                 return true;
1273             }
1274         }
1275         if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
1276             mConflictingQsExpansionGesture = false;
1277         }
1278         if (action == MotionEvent.ACTION_DOWN && isFullyCollapsed() && mQsExpansionEnabled) {
1279             mTwoFingerQsExpandPossible = true;
1280         }
1281         if (mTwoFingerQsExpandPossible && isOpenQsEvent(event) && event.getY(event.getActionIndex())
1282                 < mStatusBarMinHeight) {
1283             mMetricsLogger.count(COUNTER_PANEL_OPEN_QS, 1);
1284             mQsExpandImmediate = true;
1285             mNotificationStackScroller.setShouldShowShelfOnly(true);
1286             requestPanelHeightUpdate();
1287 
1288             // Normally, we start listening when the panel is expanded, but here we need to start
1289             // earlier so the state is already up to date when dragging down.
1290             setListening(true);
1291         }
1292         return false;
1293     }
1294 
isInQsArea(float x, float y)1295     private boolean isInQsArea(float x, float y) {
1296         return (x >= mQsFrame.getX() && x <= mQsFrame.getX() + mQsFrame.getWidth()) && (
1297                 y <= mNotificationStackScroller.getBottomMostNotificationBottom()
1298                         || y <= mQs.getView().getY() + mQs.getView().getHeight());
1299     }
1300 
isOpenQsEvent(MotionEvent event)1301     private boolean isOpenQsEvent(MotionEvent event) {
1302         final int pointerCount = event.getPointerCount();
1303         final int action = event.getActionMasked();
1304 
1305         final boolean
1306                 twoFingerDrag =
1307                 action == MotionEvent.ACTION_POINTER_DOWN && pointerCount == 2;
1308 
1309         final boolean
1310                 stylusButtonClickDrag =
1311                 action == MotionEvent.ACTION_DOWN && (event.isButtonPressed(
1312                         MotionEvent.BUTTON_STYLUS_PRIMARY) || event.isButtonPressed(
1313                         MotionEvent.BUTTON_STYLUS_SECONDARY));
1314 
1315         final boolean
1316                 mouseButtonClickDrag =
1317                 action == MotionEvent.ACTION_DOWN && (event.isButtonPressed(
1318                         MotionEvent.BUTTON_SECONDARY) || event.isButtonPressed(
1319                         MotionEvent.BUTTON_TERTIARY));
1320 
1321         return twoFingerDrag || stylusButtonClickDrag || mouseButtonClickDrag;
1322     }
1323 
handleQsDown(MotionEvent event)1324     private void handleQsDown(MotionEvent event) {
1325         if (event.getActionMasked() == MotionEvent.ACTION_DOWN && shouldQuickSettingsIntercept(
1326                 event.getX(), event.getY(), -1)) {
1327             mFalsingManager.onQsDown();
1328             mQsTracking = true;
1329             onQsExpansionStarted();
1330             mInitialHeightOnTouch = mQsExpansionHeight;
1331             mInitialTouchY = event.getX();
1332             mInitialTouchX = event.getY();
1333 
1334             // If we interrupt an expansion gesture here, make sure to update the state correctly.
1335             notifyExpandingFinished();
1336         }
1337     }
1338 
1339     /**
1340      * Input focus transfer is about to happen.
1341      */
startWaitingForOpenPanelGesture()1342     public void startWaitingForOpenPanelGesture() {
1343         if (!isFullyCollapsed()) {
1344             return;
1345         }
1346         mExpectingSynthesizedDown = true;
1347         onTrackingStarted();
1348         updatePanelExpanded();
1349     }
1350 
1351     /**
1352      * Called when this view is no longer waiting for input focus transfer.
1353      *
1354      * There are two scenarios behind this function call. First, input focus transfer
1355      * has successfully happened and this view already received synthetic DOWN event.
1356      * (mExpectingSynthesizedDown == false). Do nothing.
1357      *
1358      * Second, before input focus transfer finished, user may have lifted finger
1359      * in previous window and this window never received synthetic DOWN event.
1360      * (mExpectingSynthesizedDown == true).
1361      * In this case, we use the velocity to trigger fling event.
1362      *
1363      * @param velocity unit is in px / millis
1364      */
stopWaitingForOpenPanelGesture(boolean cancel, final float velocity)1365     public void stopWaitingForOpenPanelGesture(boolean cancel, final float velocity) {
1366         if (mExpectingSynthesizedDown) {
1367             mExpectingSynthesizedDown = false;
1368             if (cancel) {
1369                 collapse(false /* delayed */, 1.0f /* speedUpFactor */);
1370             } else {
1371                 maybeVibrateOnOpening();
1372                 fling(velocity > 1f ? 1000f * velocity : 0, true /* expand */);
1373             }
1374             onTrackingStopped(false);
1375         }
1376     }
1377 
1378     @Override
flingExpands(float vel, float vectorVel, float x, float y)1379     protected boolean flingExpands(float vel, float vectorVel, float x, float y) {
1380         boolean expands = super.flingExpands(vel, vectorVel, x, y);
1381 
1382         // If we are already running a QS expansion, make sure that we keep the panel open.
1383         if (mQsExpansionAnimator != null) {
1384             expands = true;
1385         }
1386         return expands;
1387     }
1388 
1389     @Override
shouldGestureWaitForTouchSlop()1390     protected boolean shouldGestureWaitForTouchSlop() {
1391         if (mExpectingSynthesizedDown) {
1392             mExpectingSynthesizedDown = false;
1393             return false;
1394         }
1395         return isFullyCollapsed() || mBarState != StatusBarState.SHADE;
1396     }
1397 
1398     @Override
shouldGestureIgnoreXTouchSlop(float x, float y)1399     protected boolean shouldGestureIgnoreXTouchSlop(float x, float y) {
1400         return !mAffordanceHelper.isOnAffordanceIcon(x, y);
1401     }
1402 
onQsTouch(MotionEvent event)1403     private void onQsTouch(MotionEvent event) {
1404         int pointerIndex = event.findPointerIndex(mTrackingPointer);
1405         if (pointerIndex < 0) {
1406             pointerIndex = 0;
1407             mTrackingPointer = event.getPointerId(pointerIndex);
1408         }
1409         final float y = event.getY(pointerIndex);
1410         final float x = event.getX(pointerIndex);
1411         final float h = y - mInitialTouchY;
1412 
1413         switch (event.getActionMasked()) {
1414             case MotionEvent.ACTION_DOWN:
1415                 mQsTracking = true;
1416                 mInitialTouchY = y;
1417                 mInitialTouchX = x;
1418                 onQsExpansionStarted();
1419                 mInitialHeightOnTouch = mQsExpansionHeight;
1420                 initVelocityTracker();
1421                 trackMovement(event);
1422                 break;
1423 
1424             case MotionEvent.ACTION_POINTER_UP:
1425                 final int upPointer = event.getPointerId(event.getActionIndex());
1426                 if (mTrackingPointer == upPointer) {
1427                     // gesture is ongoing, find a new pointer to track
1428                     final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1;
1429                     final float newY = event.getY(newIndex);
1430                     final float newX = event.getX(newIndex);
1431                     mTrackingPointer = event.getPointerId(newIndex);
1432                     mInitialHeightOnTouch = mQsExpansionHeight;
1433                     mInitialTouchY = newY;
1434                     mInitialTouchX = newX;
1435                 }
1436                 break;
1437 
1438             case MotionEvent.ACTION_MOVE:
1439                 setQsExpansion(h + mInitialHeightOnTouch);
1440                 if (h >= getFalsingThreshold()) {
1441                     mQsTouchAboveFalsingThreshold = true;
1442                 }
1443                 trackMovement(event);
1444                 break;
1445 
1446             case MotionEvent.ACTION_UP:
1447             case MotionEvent.ACTION_CANCEL:
1448                 mQsTracking = false;
1449                 mTrackingPointer = -1;
1450                 trackMovement(event);
1451                 float fraction = getQsExpansionFraction();
1452                 if (fraction != 0f || y >= mInitialTouchY) {
1453                     flingQsWithCurrentVelocity(y,
1454                             event.getActionMasked() == MotionEvent.ACTION_CANCEL);
1455                 }
1456                 if (mQsVelocityTracker != null) {
1457                     mQsVelocityTracker.recycle();
1458                     mQsVelocityTracker = null;
1459                 }
1460                 break;
1461         }
1462     }
1463 
getFalsingThreshold()1464     private int getFalsingThreshold() {
1465         float factor = mStatusBar.isWakeUpComingFromTouch() ? 1.5f : 1.0f;
1466         return (int) (mQsFalsingThreshold * factor);
1467     }
1468 
setOverScrolling(boolean overscrolling)1469     private void setOverScrolling(boolean overscrolling) {
1470         mStackScrollerOverscrolling = overscrolling;
1471         if (mQs == null) return;
1472         mQs.setOverscrolling(overscrolling);
1473     }
1474 
onQsExpansionStarted()1475     private void onQsExpansionStarted() {
1476         onQsExpansionStarted(0);
1477     }
1478 
onQsExpansionStarted(int overscrollAmount)1479     protected void onQsExpansionStarted(int overscrollAmount) {
1480         cancelQsAnimation();
1481         cancelHeightAnimator();
1482 
1483         // Reset scroll position and apply that position to the expanded height.
1484         float height = mQsExpansionHeight - overscrollAmount;
1485         setQsExpansion(height);
1486         requestPanelHeightUpdate();
1487         mNotificationStackScroller.checkSnoozeLeavebehind();
1488 
1489         // When expanding QS, let's authenticate the user if possible,
1490         // this will speed up notification actions.
1491         if (height == 0) {
1492             mStatusBar.requestFaceAuth();
1493         }
1494     }
1495 
setQsExpanded(boolean expanded)1496     private void setQsExpanded(boolean expanded) {
1497         boolean changed = mQsExpanded != expanded;
1498         if (changed) {
1499             mQsExpanded = expanded;
1500             updateQsState();
1501             requestPanelHeightUpdate();
1502             mFalsingManager.setQsExpanded(expanded);
1503             mStatusBar.setQsExpanded(expanded);
1504             mNotificationContainerParent.setQsExpanded(expanded);
1505             mPulseExpansionHandler.setQsExpanded(expanded);
1506             mKeyguardBypassController.setQSExpanded(expanded);
1507         }
1508     }
1509 
maybeAnimateBottomAreaAlpha()1510     private void maybeAnimateBottomAreaAlpha() {
1511         mBottomAreaShadeAlphaAnimator.cancel();
1512         if (mBarState == StatusBarState.SHADE_LOCKED) {
1513             mBottomAreaShadeAlphaAnimator.start();
1514         } else {
1515             mBottomAreaShadeAlpha = 1f;
1516         }
1517     }
1518 
1519     private final Runnable mAnimateKeyguardStatusViewInvisibleEndRunnable = new Runnable() {
1520         @Override
1521         public void run() {
1522             mKeyguardStatusViewAnimating = false;
1523             mKeyguardStatusView.setVisibility(View.INVISIBLE);
1524         }
1525     };
1526 
1527     private final Runnable mAnimateKeyguardStatusViewGoneEndRunnable = new Runnable() {
1528         @Override
1529         public void run() {
1530             mKeyguardStatusViewAnimating = false;
1531             mKeyguardStatusView.setVisibility(View.GONE);
1532         }
1533     };
1534 
1535     private final Runnable mAnimateKeyguardStatusViewVisibleEndRunnable = new Runnable() {
1536         @Override
1537         public void run() {
1538             mKeyguardStatusViewAnimating = false;
1539         }
1540     };
1541 
1542     private final Runnable mAnimateKeyguardStatusBarInvisibleEndRunnable = new Runnable() {
1543         @Override
1544         public void run() {
1545             mKeyguardStatusBar.setVisibility(View.INVISIBLE);
1546             mKeyguardStatusBar.setAlpha(1f);
1547             mKeyguardStatusBarAnimateAlpha = 1f;
1548         }
1549     };
1550 
animateKeyguardStatusBarOut()1551     private void animateKeyguardStatusBarOut() {
1552         ValueAnimator anim = ValueAnimator.ofFloat(mKeyguardStatusBar.getAlpha(), 0f);
1553         anim.addUpdateListener(mStatusBarAnimateAlphaListener);
1554         anim.setStartDelay(mKeyguardStateController.isKeyguardFadingAway()
1555                 ? mKeyguardStateController.getKeyguardFadingAwayDelay() : 0);
1556 
1557         long duration;
1558         if (mKeyguardStateController.isKeyguardFadingAway()) {
1559             duration = mKeyguardStateController.getShortenedFadingAwayDuration();
1560         } else {
1561             duration = StackStateAnimator.ANIMATION_DURATION_STANDARD;
1562         }
1563         anim.setDuration(duration);
1564 
1565         anim.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
1566         anim.addListener(new AnimatorListenerAdapter() {
1567             @Override
1568             public void onAnimationEnd(Animator animation) {
1569                 mAnimateKeyguardStatusBarInvisibleEndRunnable.run();
1570             }
1571         });
1572         anim.start();
1573     }
1574 
1575     private final ValueAnimator.AnimatorUpdateListener
1576             mStatusBarAnimateAlphaListener =
1577             new ValueAnimator.AnimatorUpdateListener() {
1578                 @Override
1579                 public void onAnimationUpdate(ValueAnimator animation) {
1580                     mKeyguardStatusBarAnimateAlpha = (float) animation.getAnimatedValue();
1581                     updateHeaderKeyguardAlpha();
1582                 }
1583             };
1584 
animateKeyguardStatusBarIn(long duration)1585     private void animateKeyguardStatusBarIn(long duration) {
1586         mKeyguardStatusBar.setVisibility(View.VISIBLE);
1587         mKeyguardStatusBar.setAlpha(0f);
1588         ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
1589         anim.addUpdateListener(mStatusBarAnimateAlphaListener);
1590         anim.setDuration(duration);
1591         anim.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
1592         anim.start();
1593     }
1594 
1595     private final Runnable mAnimateKeyguardBottomAreaInvisibleEndRunnable = new Runnable() {
1596         @Override
1597         public void run() {
1598             mKeyguardBottomArea.setVisibility(View.GONE);
1599         }
1600     };
1601 
setKeyguardBottomAreaVisibility(int statusBarState, boolean goingToFullShade)1602     private void setKeyguardBottomAreaVisibility(int statusBarState, boolean goingToFullShade) {
1603         mKeyguardBottomArea.animate().cancel();
1604         if (goingToFullShade) {
1605             mKeyguardBottomArea.animate().alpha(0f).setStartDelay(
1606                     mKeyguardStateController.getKeyguardFadingAwayDelay()).setDuration(
1607                     mKeyguardStateController.getShortenedFadingAwayDuration()).setInterpolator(
1608                     Interpolators.ALPHA_OUT).withEndAction(
1609                     mAnimateKeyguardBottomAreaInvisibleEndRunnable).start();
1610         } else if (statusBarState == StatusBarState.KEYGUARD
1611                 || statusBarState == StatusBarState.SHADE_LOCKED) {
1612             mKeyguardBottomArea.setVisibility(View.VISIBLE);
1613             mKeyguardBottomArea.setAlpha(1f);
1614         } else {
1615             mKeyguardBottomArea.setVisibility(View.GONE);
1616         }
1617     }
1618 
setKeyguardStatusViewVisibility(int statusBarState, boolean keyguardFadingAway, boolean goingToFullShade)1619     private void setKeyguardStatusViewVisibility(int statusBarState, boolean keyguardFadingAway,
1620             boolean goingToFullShade) {
1621         mKeyguardStatusView.animate().cancel();
1622         mKeyguardStatusViewAnimating = false;
1623         if ((!keyguardFadingAway && mBarState == StatusBarState.KEYGUARD
1624                 && statusBarState != StatusBarState.KEYGUARD) || goingToFullShade) {
1625             mKeyguardStatusViewAnimating = true;
1626             mKeyguardStatusView.animate().alpha(0f).setStartDelay(0).setDuration(
1627                     160).setInterpolator(Interpolators.ALPHA_OUT).withEndAction(
1628                     mAnimateKeyguardStatusViewGoneEndRunnable);
1629             if (keyguardFadingAway) {
1630                 mKeyguardStatusView.animate().setStartDelay(
1631                         mKeyguardStateController.getKeyguardFadingAwayDelay()).setDuration(
1632                         mKeyguardStateController.getShortenedFadingAwayDuration()).start();
1633             }
1634         } else if (mBarState == StatusBarState.SHADE_LOCKED
1635                 && statusBarState == StatusBarState.KEYGUARD) {
1636             mKeyguardStatusView.setVisibility(View.VISIBLE);
1637             mKeyguardStatusViewAnimating = true;
1638             mKeyguardStatusView.setAlpha(0f);
1639             mKeyguardStatusView.animate().alpha(1f).setStartDelay(0).setDuration(
1640                     320).setInterpolator(Interpolators.ALPHA_IN).withEndAction(
1641                     mAnimateKeyguardStatusViewVisibleEndRunnable);
1642         } else if (statusBarState == StatusBarState.KEYGUARD) {
1643             if (keyguardFadingAway) {
1644                 mKeyguardStatusViewAnimating = true;
1645                 mKeyguardStatusView.animate().alpha(0).translationYBy(
1646                         -getHeight() * 0.05f).setInterpolator(
1647                         Interpolators.FAST_OUT_LINEAR_IN).setDuration(125).setStartDelay(
1648                         0).withEndAction(mAnimateKeyguardStatusViewInvisibleEndRunnable).start();
1649             } else {
1650                 mKeyguardStatusView.setVisibility(View.VISIBLE);
1651                 mKeyguardStatusView.setAlpha(1f);
1652             }
1653         } else {
1654             mKeyguardStatusView.setVisibility(View.GONE);
1655             mKeyguardStatusView.setAlpha(1f);
1656         }
1657     }
1658 
updateQsState()1659     private void updateQsState() {
1660         mNotificationStackScroller.setQsExpanded(mQsExpanded);
1661         mNotificationStackScroller.setScrollingEnabled(
1662                 mBarState != StatusBarState.KEYGUARD && (!mQsExpanded
1663                         || mQsExpansionFromOverscroll));
1664         updateEmptyShadeView();
1665 
1666         mQsNavbarScrim.setVisibility(
1667                 mBarState == StatusBarState.SHADE && mQsExpanded && !mStackScrollerOverscrolling
1668                         && mQsScrimEnabled ? View.VISIBLE : View.INVISIBLE);
1669         if (mKeyguardUserSwitcher != null && mQsExpanded && !mStackScrollerOverscrolling) {
1670             mKeyguardUserSwitcher.hideIfNotSimple(true /* animate */);
1671         }
1672         if (mQs == null) return;
1673         mQs.setExpanded(mQsExpanded);
1674     }
1675 
setQsExpansion(float height)1676     private void setQsExpansion(float height) {
1677         height = Math.min(Math.max(height, mQsMinExpansionHeight), mQsMaxExpansionHeight);
1678         mQsFullyExpanded = height == mQsMaxExpansionHeight && mQsMaxExpansionHeight != 0;
1679         if (height > mQsMinExpansionHeight && !mQsExpanded && !mStackScrollerOverscrolling
1680                 && !mDozing) {
1681             setQsExpanded(true);
1682         } else if (height <= mQsMinExpansionHeight && mQsExpanded) {
1683             setQsExpanded(false);
1684         }
1685         mQsExpansionHeight = height;
1686         updateQsExpansion();
1687         requestScrollerTopPaddingUpdate(false /* animate */);
1688         updateHeaderKeyguardAlpha();
1689         if (mBarState == StatusBarState.SHADE_LOCKED || mBarState == StatusBarState.KEYGUARD) {
1690             updateKeyguardBottomAreaAlpha();
1691             updateBigClockAlpha();
1692         }
1693         if (mBarState == StatusBarState.SHADE && mQsExpanded && !mStackScrollerOverscrolling
1694                 && mQsScrimEnabled) {
1695             mQsNavbarScrim.setAlpha(getQsExpansionFraction());
1696         }
1697 
1698         if (mAccessibilityManager.isEnabled()) {
1699             mView.setAccessibilityPaneTitle(determineAccessibilityPaneTitle());
1700         }
1701 
1702         if (!mFalsingManager.isUnlockingDisabled() && mQsFullyExpanded
1703                 && mFalsingManager.shouldEnforceBouncer()) {
1704             mStatusBar.executeRunnableDismissingKeyguard(null, null /* cancelAction */,
1705                     false /* dismissShade */, true /* afterKeyguardGone */, false /* deferred */);
1706         }
1707         for (int i = 0; i < mExpansionListeners.size(); i++) {
1708             mExpansionListeners.get(i).onQsExpansionChanged(
1709                     mQsMaxExpansionHeight != 0 ? mQsExpansionHeight / mQsMaxExpansionHeight : 0);
1710         }
1711         if (DEBUG) {
1712             mView.invalidate();
1713         }
1714     }
1715 
updateQsExpansion()1716     protected void updateQsExpansion() {
1717         if (mQs == null) return;
1718         float qsExpansionFraction = getQsExpansionFraction();
1719         mQs.setQsExpansion(qsExpansionFraction, getHeaderTranslation());
1720         mMediaHierarchyManager.setQsExpansion(qsExpansionFraction);
1721         mNotificationStackScroller.setQsExpansionFraction(qsExpansionFraction);
1722     }
1723 
determineAccessibilityPaneTitle()1724     private String determineAccessibilityPaneTitle() {
1725         if (mQs != null && mQs.isCustomizing()) {
1726             return mResources.getString(R.string.accessibility_desc_quick_settings_edit);
1727         } else if (mQsExpansionHeight != 0.0f && mQsFullyExpanded) {
1728             // Upon initialisation when we are not layouted yet we don't want to announce that we
1729             // are fully expanded, hence the != 0.0f check.
1730             return mResources.getString(R.string.accessibility_desc_quick_settings);
1731         } else if (mBarState == StatusBarState.KEYGUARD) {
1732             return mResources.getString(R.string.accessibility_desc_lock_screen);
1733         } else {
1734             return mResources.getString(R.string.accessibility_desc_notification_shade);
1735         }
1736     }
1737 
calculateQsTopPadding()1738     private float calculateQsTopPadding() {
1739         if (mKeyguardShowing && (mQsExpandImmediate
1740                 || mIsExpanding && mQsExpandedWhenExpandingStarted)) {
1741 
1742             // Either QS pushes the notifications down when fully expanded, or QS is fully above the
1743             // notifications (mostly on tablets). maxNotificationPadding denotes the normal top
1744             // padding on Keyguard, maxQsPadding denotes the top padding from the quick settings
1745             // panel. We need to take the maximum and linearly interpolate with the panel expansion
1746             // for a nice motion.
1747             int maxNotificationPadding = getKeyguardNotificationStaticPadding();
1748             int maxQsPadding = mQsMaxExpansionHeight + mQsNotificationTopPadding;
1749             int max = mBarState == StatusBarState.KEYGUARD ? Math.max(
1750                     maxNotificationPadding, maxQsPadding) : maxQsPadding;
1751             return (int) MathUtils.lerp((float) mQsMinExpansionHeight, (float) max,
1752                     getExpandedFraction());
1753         } else if (mQsSizeChangeAnimator != null) {
1754             return Math.max(
1755                     (int) mQsSizeChangeAnimator.getAnimatedValue(),
1756                     getKeyguardNotificationStaticPadding());
1757         } else if (mKeyguardShowing) {
1758             // We can only do the smoother transition on Keyguard when we also are not collapsing
1759             // from a scrolled quick settings.
1760             return MathUtils.lerp((float) getKeyguardNotificationStaticPadding(),
1761                     (float) (mQsMaxExpansionHeight + mQsNotificationTopPadding),
1762                     getQsExpansionFraction());
1763         } else {
1764             return mQsExpansionHeight + mQsNotificationTopPadding;
1765         }
1766     }
1767 
1768     /**
1769      * @return the topPadding of notifications when on keyguard not respecting quick settings
1770      * expansion
1771      */
getKeyguardNotificationStaticPadding()1772     private int getKeyguardNotificationStaticPadding() {
1773         if (!mKeyguardShowing) {
1774             return 0;
1775         }
1776         if (!mKeyguardBypassController.getBypassEnabled()) {
1777             return mClockPositionResult.stackScrollerPadding;
1778         }
1779         int collapsedPosition = mHeadsUpInset;
1780         if (!mNotificationStackScroller.isPulseExpanding()) {
1781             return collapsedPosition;
1782         } else {
1783             int expandedPosition = mClockPositionResult.stackScrollerPadding;
1784             return (int) MathUtils.lerp(collapsedPosition, expandedPosition,
1785                     mNotificationStackScroller.calculateAppearFractionBypass());
1786         }
1787     }
1788 
1789 
requestScrollerTopPaddingUpdate(boolean animate)1790     protected void requestScrollerTopPaddingUpdate(boolean animate) {
1791         mNotificationStackScroller.updateTopPadding(calculateQsTopPadding(), animate);
1792         if (mKeyguardShowing && mKeyguardBypassController.getBypassEnabled()) {
1793             // update the position of the header
1794             updateQsExpansion();
1795         }
1796     }
1797 
1798 
updateQSPulseExpansion()1799     private void updateQSPulseExpansion() {
1800         if (mQs != null) {
1801             mQs.setShowCollapsedOnKeyguard(
1802                     mKeyguardShowing && mKeyguardBypassController.getBypassEnabled()
1803                             && mNotificationStackScroller.isPulseExpanding());
1804         }
1805     }
1806 
trackMovement(MotionEvent event)1807     private void trackMovement(MotionEvent event) {
1808         if (mQsVelocityTracker != null) mQsVelocityTracker.addMovement(event);
1809     }
1810 
initVelocityTracker()1811     private void initVelocityTracker() {
1812         if (mQsVelocityTracker != null) {
1813             mQsVelocityTracker.recycle();
1814         }
1815         mQsVelocityTracker = VelocityTracker.obtain();
1816     }
1817 
getCurrentQSVelocity()1818     private float getCurrentQSVelocity() {
1819         if (mQsVelocityTracker == null) {
1820             return 0;
1821         }
1822         mQsVelocityTracker.computeCurrentVelocity(1000);
1823         return mQsVelocityTracker.getYVelocity();
1824     }
1825 
cancelQsAnimation()1826     private void cancelQsAnimation() {
1827         if (mQsExpansionAnimator != null) {
1828             mQsExpansionAnimator.cancel();
1829         }
1830     }
1831 
1832     /**
1833      * @see #flingSettings(float, int, Runnable, boolean)
1834      */
flingSettings(float vel, int type)1835     public void flingSettings(float vel, int type) {
1836         flingSettings(vel, type, null, false /* isClick */);
1837     }
1838 
1839     /**
1840      * Animates QS or QQS as if the user had swiped up or down.
1841      *
1842      * @param vel              Finger velocity or 0 when not initiated by touch events.
1843      * @param type             Either {@link #FLING_EXPAND}, {@link #FLING_COLLAPSE} or {@link
1844      *                         #FLING_HIDE}.
1845      * @param onFinishRunnable Runnable to be executed at the end of animation.
1846      * @param isClick          If originated by click (different interpolator and duration.)
1847      */
flingSettings(float vel, int type, final Runnable onFinishRunnable, boolean isClick)1848     protected void flingSettings(float vel, int type, final Runnable onFinishRunnable,
1849             boolean isClick) {
1850         float target;
1851         switch (type) {
1852             case FLING_EXPAND:
1853                 target = mQsMaxExpansionHeight;
1854                 break;
1855             case FLING_COLLAPSE:
1856                 target = mQsMinExpansionHeight;
1857                 break;
1858             case FLING_HIDE:
1859             default:
1860                 target = 0;
1861         }
1862         if (target == mQsExpansionHeight) {
1863             if (onFinishRunnable != null) {
1864                 onFinishRunnable.run();
1865             }
1866             return;
1867         }
1868 
1869         // If we move in the opposite direction, reset velocity and use a different duration.
1870         boolean oppositeDirection = false;
1871         boolean expanding = type == FLING_EXPAND;
1872         if (vel > 0 && !expanding || vel < 0 && expanding) {
1873             vel = 0;
1874             oppositeDirection = true;
1875         }
1876         ValueAnimator animator = ValueAnimator.ofFloat(mQsExpansionHeight, target);
1877         if (isClick) {
1878             animator.setInterpolator(Interpolators.TOUCH_RESPONSE);
1879             animator.setDuration(368);
1880         } else {
1881             mFlingAnimationUtils.apply(animator, mQsExpansionHeight, target, vel);
1882         }
1883         if (oppositeDirection) {
1884             animator.setDuration(350);
1885         }
1886         animator.addUpdateListener(animation -> {
1887             setQsExpansion((Float) animation.getAnimatedValue());
1888         });
1889         animator.addListener(new AnimatorListenerAdapter() {
1890             @Override
1891             public void onAnimationStart(Animator animation) {
1892                 notifyExpandingStarted();
1893             }
1894 
1895             @Override
1896             public void onAnimationEnd(Animator animation) {
1897                 mAnimatingQS = false;
1898                 notifyExpandingFinished();
1899                 mNotificationStackScroller.resetCheckSnoozeLeavebehind();
1900                 mQsExpansionAnimator = null;
1901                 if (onFinishRunnable != null) {
1902                     onFinishRunnable.run();
1903                 }
1904             }
1905         });
1906         // Let's note that we're animating QS. Moving the animator here will cancel it immediately,
1907         // so we need a separate flag.
1908         mAnimatingQS = true;
1909         animator.start();
1910         mQsExpansionAnimator = animator;
1911         mQsAnimatorExpand = expanding;
1912     }
1913 
1914     /**
1915      * @return Whether we should intercept a gesture to open Quick Settings.
1916      */
shouldQuickSettingsIntercept(float x, float y, float yDiff)1917     private boolean shouldQuickSettingsIntercept(float x, float y, float yDiff) {
1918         if (!mQsExpansionEnabled || mCollapsedOnDown || (mKeyguardShowing
1919                 && mKeyguardBypassController.getBypassEnabled())) {
1920             return false;
1921         }
1922         View header = mKeyguardShowing || mQs == null ? mKeyguardStatusBar : mQs.getHeader();
1923         final boolean
1924                 onHeader =
1925                 x >= mQsFrame.getX() && x <= mQsFrame.getX() + mQsFrame.getWidth()
1926                         && y >= header.getTop() && y <= header.getBottom();
1927         if (mQsExpanded) {
1928             return onHeader || (yDiff < 0 && isInQsArea(x, y));
1929         } else {
1930             return onHeader;
1931         }
1932     }
1933 
1934     @Override
canCollapsePanelOnTouch()1935     protected boolean canCollapsePanelOnTouch() {
1936         if (!isInSettings()) {
1937             return mBarState == StatusBarState.KEYGUARD
1938                     || mNotificationStackScroller.isScrolledToBottom()
1939                     || mIsPanelCollapseOnQQS;
1940         } else {
1941             return true;
1942         }
1943     }
1944 
1945     @Override
getMaxPanelHeight()1946     protected int getMaxPanelHeight() {
1947         if (mKeyguardBypassController.getBypassEnabled() && mBarState == StatusBarState.KEYGUARD) {
1948             return getMaxPanelHeightBypass();
1949         } else {
1950             return getMaxPanelHeightNonBypass();
1951         }
1952     }
1953 
getMaxPanelHeightNonBypass()1954     private int getMaxPanelHeightNonBypass() {
1955         int min = mStatusBarMinHeight;
1956         if (!(mBarState == StatusBarState.KEYGUARD)
1957                 && mNotificationStackScroller.getNotGoneChildCount() == 0) {
1958             int minHeight = (int) (mQsMinExpansionHeight + getOverExpansionAmount());
1959             min = Math.max(min, minHeight);
1960         }
1961         int maxHeight;
1962         if (mQsExpandImmediate || mQsExpanded || mIsExpanding && mQsExpandedWhenExpandingStarted
1963                 || mPulsing) {
1964             maxHeight = calculatePanelHeightQsExpanded();
1965         } else {
1966             maxHeight = calculatePanelHeightShade();
1967         }
1968         maxHeight = Math.max(min, maxHeight);
1969         if (maxHeight == 0) {
1970             Log.wtf(TAG, "maxPanelHeight is 0. getOverExpansionAmount(): "
1971                     + getOverExpansionAmount() + ", calculatePanelHeightQsExpanded: "
1972                     + calculatePanelHeightQsExpanded() + ", calculatePanelHeightShade: "
1973                     + calculatePanelHeightShade() + ", mStatusBarMinHeight = "
1974                     + mStatusBarMinHeight + ", mQsMinExpansionHeight = " + mQsMinExpansionHeight);
1975         }
1976         return maxHeight;
1977     }
1978 
getMaxPanelHeightBypass()1979     private int getMaxPanelHeightBypass() {
1980         int position =
1981                 mClockPositionAlgorithm.getExpandedClockPosition()
1982                         + mKeyguardStatusView.getHeight();
1983         if (mNotificationStackScroller.getVisibleNotificationCount() != 0) {
1984             position += mShelfHeight / 2.0f + mDarkIconSize / 2.0f;
1985         }
1986         return position;
1987     }
1988 
isInSettings()1989     public boolean isInSettings() {
1990         return mQsExpanded;
1991     }
1992 
isExpanding()1993     public boolean isExpanding() {
1994         return mIsExpanding;
1995     }
1996 
1997     @Override
onHeightUpdated(float expandedHeight)1998     protected void onHeightUpdated(float expandedHeight) {
1999         if (!mQsExpanded || mQsExpandImmediate || mIsExpanding && mQsExpandedWhenExpandingStarted) {
2000             // Updating the clock position will set the top padding which might
2001             // trigger a new panel height and re-position the clock.
2002             // This is a circular dependency and should be avoided, otherwise we'll have
2003             // a stack overflow.
2004             if (mStackScrollerMeasuringPass > 2) {
2005                 if (DEBUG) Log.d(TAG, "Unstable notification panel height. Aborting.");
2006             } else {
2007                 positionClockAndNotifications();
2008             }
2009         }
2010         if (mQsExpandImmediate || mQsExpanded && !mQsTracking && mQsExpansionAnimator == null
2011                 && !mQsExpansionFromOverscroll) {
2012             float t;
2013             if (mKeyguardShowing) {
2014 
2015                 // On Keyguard, interpolate the QS expansion linearly to the panel expansion
2016                 t = expandedHeight / (getMaxPanelHeight());
2017             } else {
2018                 // In Shade, interpolate linearly such that QS is closed whenever panel height is
2019                 // minimum QS expansion + minStackHeight
2020                 float
2021                         panelHeightQsCollapsed =
2022                         mNotificationStackScroller.getIntrinsicPadding()
2023                                 + mNotificationStackScroller.getLayoutMinHeight();
2024                 float panelHeightQsExpanded = calculatePanelHeightQsExpanded();
2025                 t =
2026                         (expandedHeight - panelHeightQsCollapsed) / (panelHeightQsExpanded
2027                                 - panelHeightQsCollapsed);
2028             }
2029             float
2030                     targetHeight =
2031                     mQsMinExpansionHeight + t * (mQsMaxExpansionHeight - mQsMinExpansionHeight);
2032             setQsExpansion(targetHeight);
2033         }
2034         updateExpandedHeight(expandedHeight);
2035         updateHeader();
2036         updateNotificationTranslucency();
2037         updatePanelExpanded();
2038         updateGestureExclusionRect();
2039         if (DEBUG) {
2040             mView.invalidate();
2041         }
2042     }
2043 
updatePanelExpanded()2044     private void updatePanelExpanded() {
2045         boolean isExpanded = !isFullyCollapsed() || mExpectingSynthesizedDown;
2046         if (mPanelExpanded != isExpanded) {
2047             mHeadsUpManager.setIsPanelExpanded(isExpanded);
2048             mStatusBarTouchableRegionManager.setPanelExpanded(isExpanded);
2049             mStatusBar.setPanelExpanded(isExpanded);
2050             mPanelExpanded = isExpanded;
2051         }
2052     }
2053 
calculatePanelHeightShade()2054     private int calculatePanelHeightShade() {
2055         int emptyBottomMargin = mNotificationStackScroller.getEmptyBottomMargin();
2056         int maxHeight = mNotificationStackScroller.getHeight() - emptyBottomMargin;
2057         maxHeight += mNotificationStackScroller.getTopPaddingOverflow();
2058 
2059         if (mBarState == StatusBarState.KEYGUARD) {
2060             int
2061                     minKeyguardPanelBottom =
2062                     mClockPositionAlgorithm.getExpandedClockPosition()
2063                             + mKeyguardStatusView.getHeight()
2064                             + mNotificationStackScroller.getIntrinsicContentHeight();
2065             return Math.max(maxHeight, minKeyguardPanelBottom);
2066         } else {
2067             return maxHeight;
2068         }
2069     }
2070 
calculatePanelHeightQsExpanded()2071     private int calculatePanelHeightQsExpanded() {
2072         float
2073                 notificationHeight =
2074                 mNotificationStackScroller.getHeight()
2075                         - mNotificationStackScroller.getEmptyBottomMargin()
2076                         - mNotificationStackScroller.getTopPadding();
2077 
2078         // When only empty shade view is visible in QS collapsed state, simulate that we would have
2079         // it in expanded QS state as well so we don't run into troubles when fading the view in/out
2080         // and expanding/collapsing the whole panel from/to quick settings.
2081         if (mNotificationStackScroller.getNotGoneChildCount() == 0 && mShowEmptyShadeView) {
2082             notificationHeight = mNotificationStackScroller.getEmptyShadeViewHeight();
2083         }
2084         int maxQsHeight = mQsMaxExpansionHeight;
2085 
2086         if (mKeyguardShowing) {
2087             maxQsHeight += mQsNotificationTopPadding;
2088         }
2089 
2090         // If an animation is changing the size of the QS panel, take the animated value.
2091         if (mQsSizeChangeAnimator != null) {
2092             maxQsHeight = (int) mQsSizeChangeAnimator.getAnimatedValue();
2093         }
2094         float totalHeight = Math.max(maxQsHeight,
2095                 mBarState == StatusBarState.KEYGUARD ? mClockPositionResult.stackScrollerPadding
2096                         : 0) + notificationHeight
2097                 + mNotificationStackScroller.getTopPaddingOverflow();
2098         if (totalHeight > mNotificationStackScroller.getHeight()) {
2099             float
2100                     fullyCollapsedHeight =
2101                     maxQsHeight + mNotificationStackScroller.getLayoutMinHeight();
2102             totalHeight = Math.max(fullyCollapsedHeight, mNotificationStackScroller.getHeight());
2103         }
2104         return (int) totalHeight;
2105     }
2106 
updateNotificationTranslucency()2107     private void updateNotificationTranslucency() {
2108         float alpha = 1f;
2109         if (mClosingWithAlphaFadeOut && !mExpandingFromHeadsUp
2110                 && !mHeadsUpManager.hasPinnedHeadsUp()) {
2111             alpha = getFadeoutAlpha();
2112         }
2113         if (mBarState == StatusBarState.KEYGUARD && !mHintAnimationRunning
2114                 && !mKeyguardBypassController.getBypassEnabled()) {
2115             alpha *= mClockPositionResult.clockAlpha;
2116         }
2117         mNotificationStackScroller.setAlpha(alpha);
2118     }
2119 
getFadeoutAlpha()2120     private float getFadeoutAlpha() {
2121         float alpha;
2122         if (mQsMinExpansionHeight == 0) {
2123             return 1.0f;
2124         }
2125         alpha = getExpandedHeight() / mQsMinExpansionHeight;
2126         alpha = Math.max(0, Math.min(alpha, 1));
2127         alpha = (float) Math.pow(alpha, 0.75);
2128         return alpha;
2129     }
2130 
2131     @Override
getOverExpansionAmount()2132     protected float getOverExpansionAmount() {
2133         float result = mNotificationStackScroller.getCurrentOverScrollAmount(true /* top */);
2134         if (isNaN(result)) {
2135             Log.wtf(TAG, "OverExpansionAmount is NaN!");
2136         }
2137 
2138         return result;
2139     }
2140 
2141     @Override
getOverExpansionPixels()2142     protected float getOverExpansionPixels() {
2143         return mNotificationStackScroller.getCurrentOverScrolledPixels(true /* top */);
2144     }
2145 
2146     /**
2147      * Hides the header when notifications are colliding with it.
2148      */
updateHeader()2149     private void updateHeader() {
2150         if (mBarState == StatusBarState.KEYGUARD) {
2151             updateHeaderKeyguardAlpha();
2152         }
2153         updateQsExpansion();
2154     }
2155 
getHeaderTranslation()2156     protected float getHeaderTranslation() {
2157         if (mBarState == StatusBarState.KEYGUARD && !mKeyguardBypassController.getBypassEnabled()) {
2158             return -mQs.getQsMinExpansionHeight();
2159         }
2160         float appearAmount = mNotificationStackScroller.calculateAppearFraction(mExpandedHeight);
2161         float startHeight = -mQsExpansionHeight;
2162         if (mKeyguardBypassController.getBypassEnabled() && isOnKeyguard()
2163                 && mNotificationStackScroller.isPulseExpanding()) {
2164             if (!mPulseExpansionHandler.isExpanding()
2165                     && !mPulseExpansionHandler.getLeavingLockscreen()) {
2166                 // If we aborted the expansion we need to make sure the header doesn't reappear
2167                 // again after the header has animated away
2168                 appearAmount = 0;
2169             } else {
2170                 appearAmount = mNotificationStackScroller.calculateAppearFractionBypass();
2171             }
2172             startHeight = -mQs.getQsMinExpansionHeight();
2173         }
2174         float translation = MathUtils.lerp(startHeight, 0, Math.min(1.0f, appearAmount))
2175                 + mExpandOffset;
2176         return Math.min(0, translation);
2177     }
2178 
2179     /**
2180      * @return the alpha to be used to fade out the contents on Keyguard (status bar, bottom area)
2181      * during swiping up
2182      */
getKeyguardContentsAlpha()2183     private float getKeyguardContentsAlpha() {
2184         float alpha;
2185         if (mBarState == StatusBarState.KEYGUARD) {
2186 
2187             // When on Keyguard, we hide the header as soon as we expanded close enough to the
2188             // header
2189             alpha =
2190                     getExpandedHeight() / (mKeyguardStatusBar.getHeight()
2191                             + mNotificationsHeaderCollideDistance);
2192         } else {
2193 
2194             // In SHADE_LOCKED, the top card is already really close to the header. Hide it as
2195             // soon as we start translating the stack.
2196             alpha = getExpandedHeight() / mKeyguardStatusBar.getHeight();
2197         }
2198         alpha = MathUtils.saturate(alpha);
2199         alpha = (float) Math.pow(alpha, 0.75);
2200         return alpha;
2201     }
2202 
updateHeaderKeyguardAlpha()2203     private void updateHeaderKeyguardAlpha() {
2204         if (!mKeyguardShowing) {
2205             return;
2206         }
2207         float alphaQsExpansion = 1 - Math.min(1, getQsExpansionFraction() * 2);
2208         float newAlpha = Math.min(getKeyguardContentsAlpha(), alphaQsExpansion)
2209                 * mKeyguardStatusBarAnimateAlpha;
2210         newAlpha *= 1.0f - mKeyguardHeadsUpShowingAmount;
2211         mKeyguardStatusBar.setAlpha(newAlpha);
2212         boolean
2213                 hideForBypass =
2214                 mFirstBypassAttempt && mUpdateMonitor.shouldListenForFace()
2215                         || mDelayShowingKeyguardStatusBar;
2216         mKeyguardStatusBar.setVisibility(
2217                 newAlpha != 0f && !mDozing && !hideForBypass ? View.VISIBLE : View.INVISIBLE);
2218     }
2219 
updateKeyguardBottomAreaAlpha()2220     private void updateKeyguardBottomAreaAlpha() {
2221         // There are two possible panel expansion behaviors:
2222         // • User dragging up to unlock: we want to fade out as quick as possible
2223         //   (ALPHA_EXPANSION_THRESHOLD) to avoid seeing the bouncer over the bottom area.
2224         // • User tapping on lock screen: bouncer won't be visible but panel expansion will
2225         //   change due to "unlock hint animation." In this case, fading out the bottom area
2226         //   would also hide the message that says "swipe to unlock," we don't want to do that.
2227         float expansionAlpha = MathUtils.map(
2228                 isUnlockHintRunning() ? 0 : KeyguardBouncer.ALPHA_EXPANSION_THRESHOLD, 1f, 0f, 1f,
2229                 getExpandedFraction());
2230         float alpha = Math.min(expansionAlpha, 1 - getQsExpansionFraction());
2231         alpha *= mBottomAreaShadeAlpha;
2232         mKeyguardBottomArea.setAffordanceAlpha(alpha);
2233         mKeyguardBottomArea.setImportantForAccessibility(
2234                 alpha == 0f ? View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
2235                         : View.IMPORTANT_FOR_ACCESSIBILITY_AUTO);
2236         View ambientIndicationContainer = mStatusBar.getAmbientIndicationContainer();
2237         if (ambientIndicationContainer != null) {
2238             ambientIndicationContainer.setAlpha(alpha);
2239         }
2240     }
2241 
2242     /**
2243      * Custom clock fades away when user drags up to unlock or pulls down quick settings.
2244      *
2245      * Updates alpha of custom clock to match the alpha of the KeyguardBottomArea. See
2246      * {@link #updateKeyguardBottomAreaAlpha}.
2247      */
updateBigClockAlpha()2248     private void updateBigClockAlpha() {
2249         float expansionAlpha = MathUtils.map(
2250                 isUnlockHintRunning() ? 0 : KeyguardBouncer.ALPHA_EXPANSION_THRESHOLD, 1f, 0f, 1f,
2251                 getExpandedFraction());
2252         float alpha = Math.min(expansionAlpha, 1 - getQsExpansionFraction());
2253         mBigClockContainer.setAlpha(alpha);
2254     }
2255 
2256     @Override
onExpandingStarted()2257     protected void onExpandingStarted() {
2258         super.onExpandingStarted();
2259         mNotificationStackScroller.onExpansionStarted();
2260         mIsExpanding = true;
2261         mQsExpandedWhenExpandingStarted = mQsFullyExpanded;
2262         mMediaHierarchyManager.setCollapsingShadeFromQS(mQsExpandedWhenExpandingStarted &&
2263                 /* We also start expanding when flinging closed Qs. Let's exclude that */
2264                 !mAnimatingQS);
2265         if (mQsExpanded) {
2266             onQsExpansionStarted();
2267         }
2268         // Since there are QS tiles in the header now, we need to make sure we start listening
2269         // immediately so they can be up to date.
2270         if (mQs == null) return;
2271         mQs.setHeaderListening(true);
2272     }
2273 
2274     @Override
onExpandingFinished()2275     protected void onExpandingFinished() {
2276         super.onExpandingFinished();
2277         mNotificationStackScroller.onExpansionStopped();
2278         mHeadsUpManager.onExpandingFinished();
2279         mConversationNotificationManager.onNotificationPanelExpandStateChanged(isFullyCollapsed());
2280         mIsExpanding = false;
2281         mMediaHierarchyManager.setCollapsingShadeFromQS(false);
2282         if (isFullyCollapsed()) {
2283             DejankUtils.postAfterTraversal(new Runnable() {
2284                 @Override
2285                 public void run() {
2286                     setListening(false);
2287                 }
2288             });
2289 
2290             // Workaround b/22639032: Make sure we invalidate something because else RenderThread
2291             // thinks we are actually drawing a frame put in reality we don't, so RT doesn't go
2292             // ahead with rendering and we jank.
2293             mView.postOnAnimation(new Runnable() {
2294                 @Override
2295                 public void run() {
2296                     mView.getParent().invalidateChild(mView, M_DUMMY_DIRTY_RECT);
2297                 }
2298             });
2299         } else {
2300             setListening(true);
2301         }
2302         mQsExpandImmediate = false;
2303         mNotificationStackScroller.setShouldShowShelfOnly(false);
2304         mTwoFingerQsExpandPossible = false;
2305         notifyListenersTrackingHeadsUp(null);
2306         mExpandingFromHeadsUp = false;
2307         setPanelScrimMinFraction(0.0f);
2308     }
2309 
notifyListenersTrackingHeadsUp(ExpandableNotificationRow pickedChild)2310     private void notifyListenersTrackingHeadsUp(ExpandableNotificationRow pickedChild) {
2311         for (int i = 0; i < mTrackingHeadsUpListeners.size(); i++) {
2312             Consumer<ExpandableNotificationRow> listener = mTrackingHeadsUpListeners.get(i);
2313             listener.accept(pickedChild);
2314         }
2315     }
2316 
setListening(boolean listening)2317     private void setListening(boolean listening) {
2318         mKeyguardStatusBar.setListening(listening);
2319         if (mQs == null) return;
2320         mQs.setListening(listening);
2321     }
2322 
2323     @Override
expand(boolean animate)2324     public void expand(boolean animate) {
2325         super.expand(animate);
2326         setListening(true);
2327     }
2328 
2329     @Override
setOverExpansion(float overExpansion, boolean isPixels)2330     protected void setOverExpansion(float overExpansion, boolean isPixels) {
2331         if (mConflictingQsExpansionGesture || mQsExpandImmediate) {
2332             return;
2333         }
2334         if (mBarState != StatusBarState.KEYGUARD) {
2335             mNotificationStackScroller.setOnHeightChangedListener(null);
2336             if (isPixels) {
2337                 mNotificationStackScroller.setOverScrolledPixels(overExpansion, true /* onTop */,
2338                         false /* animate */);
2339             } else {
2340                 mNotificationStackScroller.setOverScrollAmount(overExpansion, true /* onTop */,
2341                         false /* animate */);
2342             }
2343             mNotificationStackScroller.setOnHeightChangedListener(mOnHeightChangedListener);
2344         }
2345     }
2346 
2347     @Override
onTrackingStarted()2348     protected void onTrackingStarted() {
2349         mFalsingManager.onTrackingStarted(!mKeyguardStateController.canDismissLockScreen());
2350         super.onTrackingStarted();
2351         if (mQsFullyExpanded) {
2352             mQsExpandImmediate = true;
2353             mNotificationStackScroller.setShouldShowShelfOnly(true);
2354         }
2355         if (mBarState == StatusBarState.KEYGUARD || mBarState == StatusBarState.SHADE_LOCKED) {
2356             mAffordanceHelper.animateHideLeftRightIcon();
2357         }
2358         mNotificationStackScroller.onPanelTrackingStarted();
2359     }
2360 
2361     @Override
onTrackingStopped(boolean expand)2362     protected void onTrackingStopped(boolean expand) {
2363         mFalsingManager.onTrackingStopped();
2364         super.onTrackingStopped(expand);
2365         if (expand) {
2366             mNotificationStackScroller.setOverScrolledPixels(0.0f, true /* onTop */,
2367                     true /* animate */);
2368         }
2369         mNotificationStackScroller.onPanelTrackingStopped();
2370         if (expand && (mBarState == StatusBarState.KEYGUARD
2371                 || mBarState == StatusBarState.SHADE_LOCKED)) {
2372             if (!mHintAnimationRunning) {
2373                 mAffordanceHelper.reset(true);
2374             }
2375         }
2376     }
2377 
updateMaxHeadsUpTranslation()2378     private void updateMaxHeadsUpTranslation() {
2379         mNotificationStackScroller.setHeadsUpBoundaries(getHeight(), mNavigationBarBottomHeight);
2380     }
2381 
2382     @Override
startUnlockHintAnimation()2383     protected void startUnlockHintAnimation() {
2384         if (mPowerManager.isPowerSaveMode()) {
2385             onUnlockHintStarted();
2386             onUnlockHintFinished();
2387             return;
2388         }
2389         super.startUnlockHintAnimation();
2390     }
2391 
2392     @Override
onUnlockHintFinished()2393     protected void onUnlockHintFinished() {
2394         super.onUnlockHintFinished();
2395         mNotificationStackScroller.setUnlockHintRunning(false);
2396     }
2397 
2398     @Override
onUnlockHintStarted()2399     protected void onUnlockHintStarted() {
2400         super.onUnlockHintStarted();
2401         mNotificationStackScroller.setUnlockHintRunning(true);
2402     }
2403 
2404     @Override
getPeekHeight()2405     protected float getPeekHeight() {
2406         if (mNotificationStackScroller.getNotGoneChildCount() > 0) {
2407             return mNotificationStackScroller.getPeekHeight();
2408         } else {
2409             return mQsMinExpansionHeight;
2410         }
2411     }
2412 
2413     @Override
shouldExpandToTopOfClearAll(float targetHeight)2414     protected boolean shouldExpandToTopOfClearAll(float targetHeight) {
2415         boolean perform = super.shouldExpandToTopOfClearAll(targetHeight);
2416         if (!perform) {
2417             return false;
2418         }
2419         // Let's make sure we're not appearing but the animation will end below the appear.
2420         // Otherwise quick settings would jump at the end of the animation.
2421         float fraction = mNotificationStackScroller.calculateAppearFraction(targetHeight);
2422         return fraction >= 1.0f;
2423     }
2424 
2425     @Override
shouldUseDismissingAnimation()2426     protected boolean shouldUseDismissingAnimation() {
2427         return mBarState != StatusBarState.SHADE && (mKeyguardStateController.canDismissLockScreen()
2428                 || !isTracking());
2429     }
2430 
2431     @Override
fullyExpandedClearAllVisible()2432     protected boolean fullyExpandedClearAllVisible() {
2433         return mNotificationStackScroller.isFooterViewNotGone()
2434                 && mNotificationStackScroller.isScrolledToBottom() && !mQsExpandImmediate;
2435     }
2436 
2437     @Override
isClearAllVisible()2438     protected boolean isClearAllVisible() {
2439         return mNotificationStackScroller.isFooterViewContentVisible();
2440     }
2441 
2442     @Override
getClearAllHeightWithPadding()2443     protected int getClearAllHeightWithPadding() {
2444         return mNotificationStackScroller.getFooterViewHeightWithPadding();
2445     }
2446 
2447     @Override
isTrackingBlocked()2448     protected boolean isTrackingBlocked() {
2449         return mConflictingQsExpansionGesture && mQsExpanded || mBlockingExpansionForCurrentTouch;
2450     }
2451 
isQsExpanded()2452     public boolean isQsExpanded() {
2453         return mQsExpanded;
2454     }
2455 
isQsDetailShowing()2456     public boolean isQsDetailShowing() {
2457         return mQs.isShowingDetail();
2458     }
2459 
closeQsDetail()2460     public void closeQsDetail() {
2461         mQs.closeDetail();
2462     }
2463 
isLaunchTransitionFinished()2464     public boolean isLaunchTransitionFinished() {
2465         return mIsLaunchTransitionFinished;
2466     }
2467 
isLaunchTransitionRunning()2468     public boolean isLaunchTransitionRunning() {
2469         return mIsLaunchTransitionRunning;
2470     }
2471 
setLaunchTransitionEndRunnable(Runnable r)2472     public void setLaunchTransitionEndRunnable(Runnable r) {
2473         mLaunchAnimationEndRunnable = r;
2474     }
2475 
updateDozingVisibilities(boolean animate)2476     private void updateDozingVisibilities(boolean animate) {
2477         mKeyguardBottomArea.setDozing(mDozing, animate);
2478         if (!mDozing && animate) {
2479             animateKeyguardStatusBarIn(StackStateAnimator.ANIMATION_DURATION_STANDARD);
2480         }
2481     }
2482 
2483     @Override
isDozing()2484     public boolean isDozing() {
2485         return mDozing;
2486     }
2487 
showEmptyShadeView(boolean emptyShadeViewVisible)2488     public void showEmptyShadeView(boolean emptyShadeViewVisible) {
2489         mShowEmptyShadeView = emptyShadeViewVisible;
2490         updateEmptyShadeView();
2491     }
2492 
updateEmptyShadeView()2493     private void updateEmptyShadeView() {
2494         // Hide "No notifications" in QS.
2495         mNotificationStackScroller.updateEmptyShadeView(mShowEmptyShadeView && !mQsExpanded);
2496     }
2497 
setQsScrimEnabled(boolean qsScrimEnabled)2498     public void setQsScrimEnabled(boolean qsScrimEnabled) {
2499         boolean changed = mQsScrimEnabled != qsScrimEnabled;
2500         mQsScrimEnabled = qsScrimEnabled;
2501         if (changed) {
2502             updateQsState();
2503         }
2504     }
2505 
setKeyguardUserSwitcher(KeyguardUserSwitcher keyguardUserSwitcher)2506     public void setKeyguardUserSwitcher(KeyguardUserSwitcher keyguardUserSwitcher) {
2507         mKeyguardUserSwitcher = keyguardUserSwitcher;
2508     }
2509 
onScreenTurningOn()2510     public void onScreenTurningOn() {
2511         mKeyguardStatusView.dozeTimeTick();
2512     }
2513 
2514     @Override
onMiddleClicked()2515     protected boolean onMiddleClicked() {
2516         switch (mBarState) {
2517             case StatusBarState.KEYGUARD:
2518                 if (!mDozingOnDown) {
2519                     if (mKeyguardBypassController.getBypassEnabled()) {
2520                         mUpdateMonitor.requestFaceAuth();
2521                     } else {
2522                         mLockscreenGestureLogger.write(MetricsEvent.ACTION_LS_HINT,
2523                                 0 /* lengthDp - N/A */, 0 /* velocityDp - N/A */);
2524                         mLockscreenGestureLogger
2525                             .log(LockscreenUiEvent.LOCKSCREEN_LOCK_SHOW_HINT);
2526                         startUnlockHintAnimation();
2527                     }
2528                 }
2529                 return true;
2530             case StatusBarState.SHADE_LOCKED:
2531                 if (!mQsExpanded) {
2532                     mStatusBarStateController.setState(StatusBarState.KEYGUARD);
2533                 }
2534                 return true;
2535             case StatusBarState.SHADE:
2536 
2537                 // This gets called in the middle of the touch handling, where the state is still
2538                 // that we are tracking the panel. Collapse the panel after this is done.
2539                 mView.post(mPostCollapseRunnable);
2540                 return false;
2541             default:
2542                 return true;
2543         }
2544     }
2545 
setPanelAlpha(int alpha, boolean animate)2546     public void setPanelAlpha(int alpha, boolean animate) {
2547         if (mPanelAlpha != alpha) {
2548             mPanelAlpha = alpha;
2549             PropertyAnimator.setProperty(mView, mPanelAlphaAnimator, alpha, alpha == 255
2550                             ? mPanelAlphaInPropertiesAnimator : mPanelAlphaOutPropertiesAnimator,
2551                     animate);
2552         }
2553     }
2554 
setPanelAlphaEndAction(Runnable r)2555     public void setPanelAlphaEndAction(Runnable r) {
2556         mPanelAlphaEndAction = r;
2557     }
2558 
updateKeyguardStatusBarForHeadsUp()2559     private void updateKeyguardStatusBarForHeadsUp() {
2560         boolean
2561                 showingKeyguardHeadsUp =
2562                 mKeyguardShowing && mHeadsUpAppearanceController.shouldBeVisible();
2563         if (mShowingKeyguardHeadsUp != showingKeyguardHeadsUp) {
2564             mShowingKeyguardHeadsUp = showingKeyguardHeadsUp;
2565             if (mKeyguardShowing) {
2566                 PropertyAnimator.setProperty(mView, KEYGUARD_HEADS_UP_SHOWING_AMOUNT,
2567                         showingKeyguardHeadsUp ? 1.0f : 0.0f, KEYGUARD_HUN_PROPERTIES,
2568                         true /* animate */);
2569             } else {
2570                 PropertyAnimator.applyImmediately(mView, KEYGUARD_HEADS_UP_SHOWING_AMOUNT, 0.0f);
2571             }
2572         }
2573     }
2574 
setKeyguardHeadsUpShowingAmount(float amount)2575     private void setKeyguardHeadsUpShowingAmount(float amount) {
2576         mKeyguardHeadsUpShowingAmount = amount;
2577         updateHeaderKeyguardAlpha();
2578     }
2579 
getKeyguardHeadsUpShowingAmount()2580     private float getKeyguardHeadsUpShowingAmount() {
2581         return mKeyguardHeadsUpShowingAmount;
2582     }
2583 
setHeadsUpAnimatingAway(boolean headsUpAnimatingAway)2584     public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) {
2585         mHeadsUpAnimatingAway = headsUpAnimatingAway;
2586         mNotificationStackScroller.setHeadsUpAnimatingAway(headsUpAnimatingAway);
2587         updateHeadsUpVisibility();
2588     }
2589 
updateHeadsUpVisibility()2590     private void updateHeadsUpVisibility() {
2591         ((PhoneStatusBarView) mBar).setHeadsUpVisible(mHeadsUpAnimatingAway || mHeadsUpPinnedMode);
2592     }
2593 
2594     @Override
setHeadsUpManager(HeadsUpManagerPhone headsUpManager)2595     public void setHeadsUpManager(HeadsUpManagerPhone headsUpManager) {
2596         super.setHeadsUpManager(headsUpManager);
2597         mHeadsUpTouchHelper = new HeadsUpTouchHelper(headsUpManager,
2598                 mNotificationStackScroller.getHeadsUpCallback(),
2599                 NotificationPanelViewController.this);
2600     }
2601 
setTrackedHeadsUp(ExpandableNotificationRow pickedChild)2602     public void setTrackedHeadsUp(ExpandableNotificationRow pickedChild) {
2603         if (pickedChild != null) {
2604             notifyListenersTrackingHeadsUp(pickedChild);
2605             mExpandingFromHeadsUp = true;
2606         }
2607         // otherwise we update the state when the expansion is finished
2608     }
2609 
2610     @Override
onClosingFinished()2611     protected void onClosingFinished() {
2612         super.onClosingFinished();
2613         resetHorizontalPanelPosition();
2614         setClosingWithAlphaFadeout(false);
2615     }
2616 
setClosingWithAlphaFadeout(boolean closing)2617     private void setClosingWithAlphaFadeout(boolean closing) {
2618         mClosingWithAlphaFadeOut = closing;
2619         mNotificationStackScroller.forceNoOverlappingRendering(closing);
2620     }
2621 
2622     /**
2623      * Updates the vertical position of the panel so it is positioned closer to the touch
2624      * responsible for opening the panel.
2625      *
2626      * @param x the x-coordinate the touch event
2627      */
updateVerticalPanelPosition(float x)2628     protected void updateVerticalPanelPosition(float x) {
2629         if (mNotificationStackScroller.getWidth() * 1.75f > mView.getWidth()) {
2630             resetHorizontalPanelPosition();
2631             return;
2632         }
2633         float leftMost = mPositionMinSideMargin + mNotificationStackScroller.getWidth() / 2;
2634         float
2635                 rightMost =
2636                 mView.getWidth() - mPositionMinSideMargin
2637                         - mNotificationStackScroller.getWidth() / 2;
2638         if (Math.abs(x - mView.getWidth() / 2) < mNotificationStackScroller.getWidth() / 4) {
2639             x = mView.getWidth() / 2;
2640         }
2641         x = Math.min(rightMost, Math.max(leftMost, x));
2642         float
2643                 center =
2644                 mNotificationStackScroller.getLeft() + mNotificationStackScroller.getWidth() / 2;
2645         setHorizontalPanelTranslation(x - center);
2646     }
2647 
resetHorizontalPanelPosition()2648     private void resetHorizontalPanelPosition() {
2649         setHorizontalPanelTranslation(0f);
2650     }
2651 
setHorizontalPanelTranslation(float translation)2652     protected void setHorizontalPanelTranslation(float translation) {
2653         mNotificationStackScroller.setTranslationX(translation);
2654         mQsFrame.setTranslationX(translation);
2655         int size = mVerticalTranslationListener.size();
2656         for (int i = 0; i < size; i++) {
2657             mVerticalTranslationListener.get(i).run();
2658         }
2659     }
2660 
updateExpandedHeight(float expandedHeight)2661     protected void updateExpandedHeight(float expandedHeight) {
2662         if (mTracking) {
2663             mNotificationStackScroller.setExpandingVelocity(getCurrentExpandVelocity());
2664         }
2665         if (mKeyguardBypassController.getBypassEnabled() && isOnKeyguard()) {
2666             // The expandedHeight is always the full panel Height when bypassing
2667             expandedHeight = getMaxPanelHeightNonBypass();
2668         }
2669         mNotificationStackScroller.setExpandedHeight(expandedHeight);
2670         updateKeyguardBottomAreaAlpha();
2671         updateBigClockAlpha();
2672         updateStatusBarIcons();
2673     }
2674 
2675     /**
2676      * @return whether the notifications are displayed full width and don't have any margins on
2677      * the side.
2678      */
isFullWidth()2679     public boolean isFullWidth() {
2680         return mIsFullWidth;
2681     }
2682 
updateStatusBarIcons()2683     private void updateStatusBarIcons() {
2684         boolean
2685                 showIconsWhenExpanded =
2686                 (isPanelVisibleBecauseOfHeadsUp() || isFullWidth())
2687                         && getExpandedHeight() < getOpeningHeight();
2688         boolean noVisibleNotifications = true;
2689         if (showIconsWhenExpanded && noVisibleNotifications && isOnKeyguard()) {
2690             showIconsWhenExpanded = false;
2691         }
2692         if (showIconsWhenExpanded != mShowIconsWhenExpanded) {
2693             mShowIconsWhenExpanded = showIconsWhenExpanded;
2694             mCommandQueue.recomputeDisableFlags(mDisplayId, false);
2695         }
2696     }
2697 
2698     private boolean isOnKeyguard() {
2699         return mBarState == StatusBarState.KEYGUARD;
2700     }
2701 
2702     public void setPanelScrimMinFraction(float minFraction) {
2703         mBar.panelScrimMinFractionChanged(minFraction);
2704     }
2705 
2706     public void clearNotificationEffects() {
2707         mStatusBar.clearNotificationEffects();
2708     }
2709 
2710     @Override
2711     protected boolean isPanelVisibleBecauseOfHeadsUp() {
2712         return (mHeadsUpManager.hasPinnedHeadsUp() || mHeadsUpAnimatingAway)
2713                 && mBarState == StatusBarState.SHADE;
2714     }
2715 
2716     public void launchCamera(boolean animate, int source) {
2717         if (source == StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP) {
2718             mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP;
2719         } else if (source == StatusBarManager.CAMERA_LAUNCH_SOURCE_WIGGLE) {
2720             mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_WIGGLE;
2721         } else if (source == StatusBarManager.CAMERA_LAUNCH_SOURCE_LIFT_TRIGGER) {
2722             mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_LIFT_TRIGGER;
2723         } else {
2724 
2725             // Default.
2726             mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_AFFORDANCE;
2727         }
2728 
2729         // If we are launching it when we are occluded already we don't want it to animate,
2730         // nor setting these flags, since the occluded state doesn't change anymore, hence it's
2731         // never reset.
2732         if (!isFullyCollapsed()) {
2733             setLaunchingAffordance(true);
2734         } else {
2735             animate = false;
2736         }
2737         mAffordanceHasPreview = mKeyguardBottomArea.getRightPreview() != null;
2738         mAffordanceHelper.launchAffordance(
2739                 animate, mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL);
2740     }
2741 
2742     public void onAffordanceLaunchEnded() {
2743         setLaunchingAffordance(false);
2744     }
2745 
2746     /**
2747      * Set whether we are currently launching an affordance. This is currently only set when
2748      * launched via a camera gesture.
2749      */
2750     private void setLaunchingAffordance(boolean launchingAffordance) {
2751         mLaunchingAffordance = launchingAffordance;
2752         mKeyguardAffordanceHelperCallback.getLeftIcon().setLaunchingAffordance(launchingAffordance);
2753         mKeyguardAffordanceHelperCallback.getRightIcon().setLaunchingAffordance(
2754                 launchingAffordance);
2755         mKeyguardBypassController.setLaunchingAffordance(launchingAffordance);
2756         if (mAffordanceLaunchListener != null) {
2757             mAffordanceLaunchListener.accept(launchingAffordance);
2758         }
2759     }
2760 
2761     /**
2762      * Return true when a bottom affordance is launching an occluded activity with a splash screen.
2763      */
2764     public boolean isLaunchingAffordanceWithPreview() {
2765         return mLaunchingAffordance && mAffordanceHasPreview;
2766     }
2767 
2768     /**
2769      * Whether the camera application can be launched for the camera launch gesture.
2770      */
2771     public boolean canCameraGestureBeLaunched() {
2772         if (!mStatusBar.isCameraAllowedByAdmin()) {
2773             return false;
2774         }
2775 
2776         ResolveInfo resolveInfo = mKeyguardBottomArea.resolveCameraIntent();
2777         String
2778                 packageToLaunch =
2779                 (resolveInfo == null || resolveInfo.activityInfo == null) ? null
2780                         : resolveInfo.activityInfo.packageName;
2781         return packageToLaunch != null && (mBarState != StatusBarState.SHADE || !isForegroundApp(
2782                 packageToLaunch)) && !mAffordanceHelper.isSwipingInProgress();
2783     }
2784 
2785     /**
2786      * Return true if the applications with the package name is running in foreground.
2787      *
2788      * @param pkgName application package name.
2789      */
2790     private boolean isForegroundApp(String pkgName) {
2791         List<ActivityManager.RunningTaskInfo> tasks = mActivityManager.getRunningTasks(1);
2792         return !tasks.isEmpty() && pkgName.equals(tasks.get(0).topActivity.getPackageName());
2793     }
2794 
2795     private void setGroupManager(NotificationGroupManager groupManager) {
2796         mGroupManager = groupManager;
2797     }
2798 
2799     public boolean hideStatusBarIconsWhenExpanded() {
2800         if (mLaunchingNotification) {
2801             return mHideIconsDuringNotificationLaunch;
2802         }
2803         if (mHeadsUpAppearanceController != null
2804                 && mHeadsUpAppearanceController.shouldBeVisible()) {
2805             return false;
2806         }
2807         return !isFullWidth() || !mShowIconsWhenExpanded;
2808     }
2809 
2810     private final FragmentListener mFragmentListener = new FragmentListener() {
2811         @Override
2812         public void onFragmentViewCreated(String tag, Fragment fragment) {
2813             mQs = (QS) fragment;
2814             mQs.setPanelView(mHeightListener);
2815             mQs.setExpandClickListener(mOnClickListener);
2816             mQs.setHeaderClickable(mQsExpansionEnabled);
2817             updateQSPulseExpansion();
2818             mQs.setOverscrolling(mStackScrollerOverscrolling);
2819 
2820             // recompute internal state when qspanel height changes
2821             mQs.getView().addOnLayoutChangeListener(
2822                     (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
2823                         final int height = bottom - top;
2824                         final int oldHeight = oldBottom - oldTop;
2825                         if (height != oldHeight) {
2826                             mHeightListener.onQsHeightChanged();
2827                         }
2828                     });
2829             mNotificationStackScroller.setQsContainer((ViewGroup) mQs.getView());
2830             if (mQs instanceof QSFragment) {
2831                 mKeyguardStatusBar.setQSPanel(((QSFragment) mQs).getQsPanel());
2832             }
2833             updateQsExpansion();
2834         }
2835 
2836         @Override
onFragmentViewDestroyed(String tag, Fragment fragment)2837         public void onFragmentViewDestroyed(String tag, Fragment fragment) {
2838             // Manual handling of fragment lifecycle is only required because this bridges
2839             // non-fragment and fragment code. Once we are using a fragment for the notification
2840             // panel, mQs will not need to be null cause it will be tied to the same lifecycle.
2841             if (fragment == mQs) {
2842                 mQs = null;
2843             }
2844         }
2845     };
2846 
2847     @Override
setTouchAndAnimationDisabled(boolean disabled)2848     public void setTouchAndAnimationDisabled(boolean disabled) {
2849         super.setTouchAndAnimationDisabled(disabled);
2850         if (disabled && mAffordanceHelper.isSwipingInProgress() && !mIsLaunchTransitionRunning) {
2851             mAffordanceHelper.reset(false /* animate */);
2852         }
2853         mNotificationStackScroller.setAnimationsEnabled(!disabled);
2854     }
2855 
2856     /**
2857      * Sets the dozing state.
2858      *
2859      * @param dozing              {@code true} when dozing.
2860      * @param animate             if transition should be animated.
2861      * @param wakeUpTouchLocation touch event location - if woken up by SLPI sensor.
2862      */
setDozing(boolean dozing, boolean animate, PointF wakeUpTouchLocation)2863     public void setDozing(boolean dozing, boolean animate, PointF wakeUpTouchLocation) {
2864         if (dozing == mDozing) return;
2865         mView.setDozing(dozing);
2866         mDozing = dozing;
2867         mNotificationStackScroller.setDozing(mDozing, animate, wakeUpTouchLocation);
2868         mKeyguardBottomArea.setDozing(mDozing, animate);
2869 
2870         if (dozing) {
2871             mBottomAreaShadeAlphaAnimator.cancel();
2872         }
2873 
2874         if (mBarState == StatusBarState.KEYGUARD || mBarState == StatusBarState.SHADE_LOCKED) {
2875             updateDozingVisibilities(animate);
2876         }
2877 
2878         final float dozeAmount = dozing ? 1 : 0;
2879         mStatusBarStateController.setDozeAmount(dozeAmount, animate);
2880     }
2881 
setPulsing(boolean pulsing)2882     public void setPulsing(boolean pulsing) {
2883         mPulsing = pulsing;
2884         final boolean
2885                 animatePulse =
2886                 !mDozeParameters.getDisplayNeedsBlanking() && mDozeParameters.getAlwaysOn();
2887         if (animatePulse) {
2888             mAnimateNextPositionUpdate = true;
2889         }
2890         // Do not animate the clock when waking up from a pulse.
2891         // The height callback will take care of pushing the clock to the right position.
2892         if (!mPulsing && !mDozing) {
2893             mAnimateNextPositionUpdate = false;
2894         }
2895         mNotificationStackScroller.setPulsing(pulsing, animatePulse);
2896         mKeyguardStatusView.setPulsing(pulsing);
2897     }
2898 
setAmbientIndicationBottomPadding(int ambientIndicationBottomPadding)2899     public void setAmbientIndicationBottomPadding(int ambientIndicationBottomPadding) {
2900         if (mAmbientIndicationBottomPadding != ambientIndicationBottomPadding) {
2901             mAmbientIndicationBottomPadding = ambientIndicationBottomPadding;
2902             mStatusBar.updateKeyguardMaxNotifications();
2903         }
2904     }
2905 
dozeTimeTick()2906     public void dozeTimeTick() {
2907         mKeyguardBottomArea.dozeTimeTick();
2908         mKeyguardStatusView.dozeTimeTick();
2909         if (mInterpolatedDarkAmount > 0) {
2910             positionClockAndNotifications();
2911         }
2912     }
2913 
setStatusAccessibilityImportance(int mode)2914     public void setStatusAccessibilityImportance(int mode) {
2915         mKeyguardStatusView.setImportantForAccessibility(mode);
2916     }
2917 
2918     /**
2919      * TODO: this should be removed.
2920      * It's not correct to pass this view forward because other classes will end up adding
2921      * children to it. Theme will be out of sync.
2922      *
2923      * @return bottom area view
2924      */
getKeyguardBottomAreaView()2925     public KeyguardBottomAreaView getKeyguardBottomAreaView() {
2926         return mKeyguardBottomArea;
2927     }
2928 
setUserSetupComplete(boolean userSetupComplete)2929     public void setUserSetupComplete(boolean userSetupComplete) {
2930         mUserSetupComplete = userSetupComplete;
2931         mKeyguardBottomArea.setUserSetupComplete(userSetupComplete);
2932     }
2933 
applyExpandAnimationParams(ExpandAnimationParameters params)2934     public void applyExpandAnimationParams(ExpandAnimationParameters params) {
2935         mExpandOffset = params != null ? params.getTopChange() : 0;
2936         updateQsExpansion();
2937         if (params != null) {
2938             boolean hideIcons = params.getProgress(
2939                     ActivityLaunchAnimator.ANIMATION_DELAY_ICON_FADE_IN, 100) == 0.0f;
2940             if (hideIcons != mHideIconsDuringNotificationLaunch) {
2941                 mHideIconsDuringNotificationLaunch = hideIcons;
2942                 if (!hideIcons) {
2943                     mCommandQueue.recomputeDisableFlags(mDisplayId, true /* animate */);
2944                 }
2945             }
2946         }
2947     }
2948 
addTrackingHeadsUpListener(Consumer<ExpandableNotificationRow> listener)2949     public void addTrackingHeadsUpListener(Consumer<ExpandableNotificationRow> listener) {
2950         mTrackingHeadsUpListeners.add(listener);
2951     }
2952 
removeTrackingHeadsUpListener(Consumer<ExpandableNotificationRow> listener)2953     public void removeTrackingHeadsUpListener(Consumer<ExpandableNotificationRow> listener) {
2954         mTrackingHeadsUpListeners.remove(listener);
2955     }
2956 
addVerticalTranslationListener(Runnable verticalTranslationListener)2957     public void addVerticalTranslationListener(Runnable verticalTranslationListener) {
2958         mVerticalTranslationListener.add(verticalTranslationListener);
2959     }
2960 
removeVerticalTranslationListener(Runnable verticalTranslationListener)2961     public void removeVerticalTranslationListener(Runnable verticalTranslationListener) {
2962         mVerticalTranslationListener.remove(verticalTranslationListener);
2963     }
2964 
setHeadsUpAppearanceController( HeadsUpAppearanceController headsUpAppearanceController)2965     public void setHeadsUpAppearanceController(
2966             HeadsUpAppearanceController headsUpAppearanceController) {
2967         mHeadsUpAppearanceController = headsUpAppearanceController;
2968     }
2969 
2970     /**
2971      * Starts the animation before we dismiss Keyguard, i.e. an disappearing animation on the
2972      * security view of the bouncer.
2973      */
onBouncerPreHideAnimation()2974     public void onBouncerPreHideAnimation() {
2975         setKeyguardStatusViewVisibility(mBarState, true /* keyguardFadingAway */,
2976                 false /* goingToFullShade */);
2977     }
2978 
2979     /**
2980      * Do not let the user drag the shade up and down for the current touch session.
2981      * This is necessary to avoid shade expansion while/after the bouncer is dismissed.
2982      */
blockExpansionForCurrentTouch()2983     public void blockExpansionForCurrentTouch() {
2984         mBlockingExpansionForCurrentTouch = mTracking;
2985     }
2986 
2987     @Override
dump(FileDescriptor fd, PrintWriter pw, String[] args)2988     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
2989         super.dump(fd, pw, args);
2990         pw.println("    gestureExclusionRect: " + calculateGestureExclusionRect());
2991         if (mKeyguardStatusBar != null) {
2992             mKeyguardStatusBar.dump(fd, pw, args);
2993         }
2994         if (mKeyguardStatusView != null) {
2995             mKeyguardStatusView.dump(fd, pw, args);
2996         }
2997     }
2998 
hasActiveClearableNotifications()2999     public boolean hasActiveClearableNotifications() {
3000         return mNotificationStackScroller.hasActiveClearableNotifications(ROWS_ALL);
3001     }
3002 
updateShowEmptyShadeView()3003     private void updateShowEmptyShadeView() {
3004         boolean
3005                 showEmptyShadeView =
3006                 mBarState != StatusBarState.KEYGUARD && !mEntryManager.hasActiveNotifications();
3007         showEmptyShadeView(showEmptyShadeView);
3008     }
3009 
createRemoteInputDelegate()3010     public RemoteInputController.Delegate createRemoteInputDelegate() {
3011         return mNotificationStackScroller.createDelegate();
3012     }
3013 
updateNotificationViews(String reason)3014     void updateNotificationViews(String reason) {
3015         mNotificationStackScroller.updateSectionBoundaries(reason);
3016         mNotificationStackScroller.updateSpeedBumpIndex();
3017         mNotificationStackScroller.updateFooter();
3018         updateShowEmptyShadeView();
3019         mNotificationStackScroller.updateIconAreaViews();
3020     }
3021 
onUpdateRowStates()3022     public void onUpdateRowStates() {
3023         mNotificationStackScroller.onUpdateRowStates();
3024     }
3025 
hasPulsingNotifications()3026     public boolean hasPulsingNotifications() {
3027         return mNotificationStackScroller.hasPulsingNotifications();
3028     }
3029 
getActivatedChild()3030     public ActivatableNotificationView getActivatedChild() {
3031         return mNotificationStackScroller.getActivatedChild();
3032     }
3033 
setActivatedChild(ActivatableNotificationView o)3034     public void setActivatedChild(ActivatableNotificationView o) {
3035         mNotificationStackScroller.setActivatedChild(o);
3036     }
3037 
runAfterAnimationFinished(Runnable r)3038     public void runAfterAnimationFinished(Runnable r) {
3039         mNotificationStackScroller.runAfterAnimationFinished(r);
3040     }
3041 
setScrollingEnabled(boolean b)3042     public void setScrollingEnabled(boolean b) {
3043         mNotificationStackScroller.setScrollingEnabled(b);
3044     }
3045 
initDependencies(StatusBar statusBar, NotificationGroupManager groupManager, NotificationShelf notificationShelf, NotificationIconAreaController notificationIconAreaController, ScrimController scrimController)3046     public void initDependencies(StatusBar statusBar, NotificationGroupManager groupManager,
3047             NotificationShelf notificationShelf,
3048             NotificationIconAreaController notificationIconAreaController,
3049             ScrimController scrimController) {
3050         setStatusBar(statusBar);
3051         setGroupManager(mGroupManager);
3052         mNotificationStackScroller.setNotificationPanelController(this);
3053         mNotificationStackScroller.setIconAreaController(notificationIconAreaController);
3054         mNotificationStackScroller.setStatusBar(statusBar);
3055         mNotificationStackScroller.setGroupManager(groupManager);
3056         mNotificationStackScroller.setShelf(notificationShelf);
3057         mNotificationStackScroller.setScrimController(scrimController);
3058         updateShowEmptyShadeView();
3059     }
3060 
showTransientIndication(int id)3061     public void showTransientIndication(int id) {
3062         mKeyguardIndicationController.showTransientIndication(id);
3063     }
3064 
setOnReinflationListener(Runnable onReinflationListener)3065     public void setOnReinflationListener(Runnable onReinflationListener) {
3066         mOnReinflationListener = onReinflationListener;
3067     }
3068 
setAlpha(float alpha)3069     public void setAlpha(float alpha) {
3070         mView.setAlpha(alpha);
3071     }
3072 
fadeOut(long startDelayMs, long durationMs, Runnable endAction)3073     public ViewPropertyAnimator fadeOut(long startDelayMs, long durationMs, Runnable endAction) {
3074         return mView.animate().alpha(0).setStartDelay(startDelayMs).setDuration(
3075                 durationMs).setInterpolator(Interpolators.ALPHA_OUT).withLayer().withEndAction(
3076                 endAction);
3077     }
3078 
resetViewGroupFade()3079     public void resetViewGroupFade() {
3080         ViewGroupFadeHelper.reset(mView);
3081     }
3082 
addOnGlobalLayoutListener(ViewTreeObserver.OnGlobalLayoutListener listener)3083     public void addOnGlobalLayoutListener(ViewTreeObserver.OnGlobalLayoutListener listener) {
3084         mView.getViewTreeObserver().addOnGlobalLayoutListener(listener);
3085     }
3086 
removeOnGlobalLayoutListener(ViewTreeObserver.OnGlobalLayoutListener listener)3087     public void removeOnGlobalLayoutListener(ViewTreeObserver.OnGlobalLayoutListener listener) {
3088         mView.getViewTreeObserver().removeOnGlobalLayoutListener(listener);
3089     }
3090 
getOnHeadsUpChangedListener()3091     public MyOnHeadsUpChangedListener getOnHeadsUpChangedListener() {
3092         return mOnHeadsUpChangedListener;
3093     }
3094 
getHeight()3095     public int getHeight() {
3096         return mView.getHeight();
3097     }
3098 
getHeaderDebugInfo()3099     public TextView getHeaderDebugInfo() {
3100         return mView.findViewById(R.id.header_debug_info);
3101     }
3102 
onThemeChanged()3103     public void onThemeChanged() {
3104         mConfigurationListener.onThemeChanged();
3105     }
3106 
3107     @Override
createLayoutChangeListener()3108     public OnLayoutChangeListener createLayoutChangeListener() {
3109         return new OnLayoutChangeListener();
3110     }
3111 
setEmptyDragAmount(float amount)3112     public void setEmptyDragAmount(float amount) {
3113         mExpansionCallback.setEmptyDragAmount(amount);
3114     }
3115 
3116     @Override
createTouchHandler()3117     protected TouchHandler createTouchHandler() {
3118         return new TouchHandler() {
3119             @Override
3120             public boolean onInterceptTouchEvent(MotionEvent event) {
3121                 if (mBlockTouches || mQsFullyExpanded && mQs.disallowPanelTouches()) {
3122                     return false;
3123                 }
3124                 initDownStates(event);
3125                 // Do not let touches go to shade or QS if the bouncer is visible,
3126                 // but still let user swipe down to expand the panel, dismissing the bouncer.
3127                 if (mStatusBar.isBouncerShowing()) {
3128                     return true;
3129                 }
3130                 if (mBar.panelEnabled() && mHeadsUpTouchHelper.onInterceptTouchEvent(event)) {
3131                     mMetricsLogger.count(COUNTER_PANEL_OPEN, 1);
3132                     mMetricsLogger.count(COUNTER_PANEL_OPEN_PEEK, 1);
3133                     return true;
3134                 }
3135                 if (!shouldQuickSettingsIntercept(mDownX, mDownY, 0)
3136                         && mPulseExpansionHandler.onInterceptTouchEvent(event)) {
3137                     return true;
3138                 }
3139 
3140                 if (!isFullyCollapsed() && onQsIntercept(event)) {
3141                     return true;
3142                 }
3143                 return super.onInterceptTouchEvent(event);
3144             }
3145 
3146             @Override
3147             public boolean onTouch(View v, MotionEvent event) {
3148                 if (mBlockTouches || (mQsFullyExpanded && mQs != null
3149                         && mQs.disallowPanelTouches())) {
3150                     return false;
3151                 }
3152 
3153                 // Do not allow panel expansion if bouncer is scrimmed, otherwise user would be able
3154                 // to pull down QS or expand the shade.
3155                 if (mStatusBar.isBouncerShowingScrimmed()) {
3156                     return false;
3157                 }
3158 
3159                 // Make sure the next touch won't the blocked after the current ends.
3160                 if (event.getAction() == MotionEvent.ACTION_UP
3161                         || event.getAction() == MotionEvent.ACTION_CANCEL) {
3162                     mBlockingExpansionForCurrentTouch = false;
3163                 }
3164                 // When touch focus transfer happens, ACTION_DOWN->ACTION_UP may happen immediately
3165                 // without any ACTION_MOVE event.
3166                 // In such case, simply expand the panel instead of being stuck at the bottom bar.
3167                 if (mLastEventSynthesizedDown && event.getAction() == MotionEvent.ACTION_UP) {
3168                     expand(true /* animate */);
3169                 }
3170                 initDownStates(event);
3171                 if (!mIsExpanding && !shouldQuickSettingsIntercept(mDownX, mDownY, 0)
3172                         && mPulseExpansionHandler.onTouchEvent(event)) {
3173                     // We're expanding all the other ones shouldn't get this anymore
3174                     return true;
3175                 }
3176                 if (mListenForHeadsUp && !mHeadsUpTouchHelper.isTrackingHeadsUp()
3177                         && mHeadsUpTouchHelper.onInterceptTouchEvent(event)) {
3178                     mMetricsLogger.count(COUNTER_PANEL_OPEN_PEEK, 1);
3179                 }
3180                 boolean handled = false;
3181                 if ((!mIsExpanding || mHintAnimationRunning) && !mQsExpanded
3182                         && mBarState != StatusBarState.SHADE && !mDozing) {
3183                     handled |= mAffordanceHelper.onTouchEvent(event);
3184                 }
3185                 if (mOnlyAffordanceInThisMotion) {
3186                     return true;
3187                 }
3188                 handled |= mHeadsUpTouchHelper.onTouchEvent(event);
3189 
3190                 if (!mHeadsUpTouchHelper.isTrackingHeadsUp() && handleQsTouch(event)) {
3191                     return true;
3192                 }
3193                 if (event.getActionMasked() == MotionEvent.ACTION_DOWN && isFullyCollapsed()) {
3194                     mMetricsLogger.count(COUNTER_PANEL_OPEN, 1);
3195                     updateVerticalPanelPosition(event.getX());
3196                     handled = true;
3197                 }
3198                 handled |= super.onTouch(v, event);
3199                 return !mDozing || mPulsing || handled;
3200             }
3201         };
3202     }
3203 
3204     @Override
3205     protected PanelViewController.OnConfigurationChangedListener
3206             createOnConfigurationChangedListener() {
3207         return new OnConfigurationChangedListener();
3208     }
3209 
3210     private class OnHeightChangedListener implements ExpandableView.OnHeightChangedListener {
3211         @Override
3212         public void onHeightChanged(ExpandableView view, boolean needsAnimation) {
3213 
3214             // Block update if we are in quick settings and just the top padding changed
3215             // (i.e. view == null).
3216             if (view == null && mQsExpanded) {
3217                 return;
3218             }
3219             if (needsAnimation && mInterpolatedDarkAmount == 0) {
3220                 mAnimateNextPositionUpdate = true;
3221             }
3222             ExpandableView firstChildNotGone = mNotificationStackScroller.getFirstChildNotGone();
3223             ExpandableNotificationRow
3224                     firstRow =
3225                     firstChildNotGone instanceof ExpandableNotificationRow
3226                             ? (ExpandableNotificationRow) firstChildNotGone : null;
3227             if (firstRow != null && (view == firstRow || (firstRow.getNotificationParent()
3228                     == firstRow))) {
3229                 requestScrollerTopPaddingUpdate(false /* animate */);
3230             }
3231             requestPanelHeightUpdate();
3232         }
3233 
3234         @Override
3235         public void onReset(ExpandableView view) {
3236         }
3237     }
3238 
3239     private class OnClickListener implements View.OnClickListener {
3240         @Override
3241         public void onClick(View v) {
3242             onQsExpansionStarted();
3243             if (mQsExpanded) {
3244                 flingSettings(0 /* vel */, FLING_COLLAPSE, null /* onFinishRunnable */,
3245                         true /* isClick */);
3246             } else if (mQsExpansionEnabled) {
3247                 mLockscreenGestureLogger.write(MetricsEvent.ACTION_SHADE_QS_TAP, 0, 0);
3248                 flingSettings(0 /* vel */, FLING_EXPAND, null /* onFinishRunnable */,
3249                         true /* isClick */);
3250             }
3251         }
3252     }
3253 
3254     private class OnOverscrollTopChangedListener implements
3255             NotificationStackScrollLayout.OnOverscrollTopChangedListener {
3256         @Override
3257         public void onOverscrollTopChanged(float amount, boolean isRubberbanded) {
3258             cancelQsAnimation();
3259             if (!mQsExpansionEnabled) {
3260                 amount = 0f;
3261             }
3262             float rounded = amount >= 1f ? amount : 0f;
3263             setOverScrolling(rounded != 0f && isRubberbanded);
3264             mQsExpansionFromOverscroll = rounded != 0f;
3265             mLastOverscroll = rounded;
3266             updateQsState();
3267             setQsExpansion(mQsMinExpansionHeight + rounded);
3268         }
3269 
3270         @Override
3271         public void flingTopOverscroll(float velocity, boolean open) {
3272             mLastOverscroll = 0f;
3273             mQsExpansionFromOverscroll = false;
3274             setQsExpansion(mQsExpansionHeight);
3275             flingSettings(!mQsExpansionEnabled && open ? 0f : velocity,
3276                     open && mQsExpansionEnabled ? FLING_EXPAND : FLING_COLLAPSE, () -> {
3277                         mStackScrollerOverscrolling = false;
3278                         setOverScrolling(false);
3279                         updateQsState();
3280                     }, false /* isClick */);
3281         }
3282     }
3283 
3284     private class DynamicPrivacyControlListener implements DynamicPrivacyController.Listener {
3285         @Override
3286         public void onDynamicPrivacyChanged() {
3287             // Do not request animation when pulsing or waking up, otherwise the clock wiill be out
3288             // of sync with the notification panel.
3289             if (mLinearDarkAmount != 0) {
3290                 return;
3291             }
3292             mAnimateNextPositionUpdate = true;
3293         }
3294     }
3295 
3296     private class KeyguardAffordanceHelperCallback implements KeyguardAffordanceHelper.Callback {
3297         @Override
3298         public void onAnimationToSideStarted(boolean rightPage, float translation, float vel) {
3299             boolean
3300                     start =
3301                     mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL ? rightPage
3302                             : !rightPage;
3303             mIsLaunchTransitionRunning = true;
3304             mLaunchAnimationEndRunnable = null;
3305             float displayDensity = mStatusBar.getDisplayDensity();
3306             int lengthDp = Math.abs((int) (translation / displayDensity));
3307             int velocityDp = Math.abs((int) (vel / displayDensity));
3308             if (start) {
3309                 mLockscreenGestureLogger.write(MetricsEvent.ACTION_LS_DIALER, lengthDp, velocityDp);
3310                 mLockscreenGestureLogger.log(LockscreenUiEvent.LOCKSCREEN_DIALER);
3311                 mFalsingManager.onLeftAffordanceOn();
3312                 if (mFalsingManager.shouldEnforceBouncer()) {
3313                     mStatusBar.executeRunnableDismissingKeyguard(
3314                             () -> mKeyguardBottomArea.launchLeftAffordance(), null,
3315                             true /* dismissShade */, false /* afterKeyguardGone */,
3316                             true /* deferred */);
3317                 } else {
3318                     mKeyguardBottomArea.launchLeftAffordance();
3319                 }
3320             } else {
3321                 if (KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_AFFORDANCE.equals(
3322                         mLastCameraLaunchSource)) {
3323                     mLockscreenGestureLogger.write(
3324                             MetricsEvent.ACTION_LS_CAMERA, lengthDp, velocityDp);
3325                     mLockscreenGestureLogger.log(LockscreenUiEvent.LOCKSCREEN_CAMERA);
3326                 }
3327                 mFalsingManager.onCameraOn();
3328                 if (mFalsingManager.shouldEnforceBouncer()) {
3329                     mStatusBar.executeRunnableDismissingKeyguard(
3330                             () -> mKeyguardBottomArea.launchCamera(mLastCameraLaunchSource), null,
3331                             true /* dismissShade */, false /* afterKeyguardGone */,
3332                             true /* deferred */);
3333                 } else {
3334                     mKeyguardBottomArea.launchCamera(mLastCameraLaunchSource);
3335                 }
3336             }
3337             mStatusBar.startLaunchTransitionTimeout();
3338             mBlockTouches = true;
3339         }
3340 
3341         @Override
3342         public void onAnimationToSideEnded() {
3343             mIsLaunchTransitionRunning = false;
3344             mIsLaunchTransitionFinished = true;
3345             if (mLaunchAnimationEndRunnable != null) {
3346                 mLaunchAnimationEndRunnable.run();
3347                 mLaunchAnimationEndRunnable = null;
3348             }
3349             mStatusBar.readyForKeyguardDone();
3350         }
3351 
3352         @Override
3353         public float getMaxTranslationDistance() {
3354             return (float) Math.hypot(mView.getWidth(), getHeight());
3355         }
3356 
3357         @Override
3358         public void onSwipingStarted(boolean rightIcon) {
3359             mFalsingManager.onAffordanceSwipingStarted(rightIcon);
3360             boolean
3361                     camera =
3362                     mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL ? !rightIcon
3363                             : rightIcon;
3364             if (camera) {
3365                 mKeyguardBottomArea.bindCameraPrewarmService();
3366             }
3367             mView.requestDisallowInterceptTouchEvent(true);
3368             mOnlyAffordanceInThisMotion = true;
3369             mQsTracking = false;
3370         }
3371 
3372         @Override
3373         public void onSwipingAborted() {
3374             mFalsingManager.onAffordanceSwipingAborted();
3375             mKeyguardBottomArea.unbindCameraPrewarmService(false /* launched */);
3376         }
3377 
3378         @Override
3379         public void onIconClicked(boolean rightIcon) {
3380             if (mHintAnimationRunning) {
3381                 return;
3382             }
3383             mHintAnimationRunning = true;
3384             mAffordanceHelper.startHintAnimation(rightIcon, () -> {
3385                 mHintAnimationRunning = false;
3386                 mStatusBar.onHintFinished();
3387             });
3388             rightIcon =
3389                     mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL ? !rightIcon
3390                             : rightIcon;
3391             if (rightIcon) {
3392                 mStatusBar.onCameraHintStarted();
3393             } else {
3394                 if (mKeyguardBottomArea.isLeftVoiceAssist()) {
3395                     mStatusBar.onVoiceAssistHintStarted();
3396                 } else {
3397                     mStatusBar.onPhoneHintStarted();
3398                 }
3399             }
3400         }
3401 
3402         @Override
3403         public KeyguardAffordanceView getLeftIcon() {
3404             return mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL
3405                     ? mKeyguardBottomArea.getRightView() : mKeyguardBottomArea.getLeftView();
3406         }
3407 
3408         @Override
3409         public KeyguardAffordanceView getRightIcon() {
3410             return mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL
3411                     ? mKeyguardBottomArea.getLeftView() : mKeyguardBottomArea.getRightView();
3412         }
3413 
3414         @Override
3415         public View getLeftPreview() {
3416             return mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL
3417                     ? mKeyguardBottomArea.getRightPreview() : mKeyguardBottomArea.getLeftPreview();
3418         }
3419 
3420         @Override
3421         public View getRightPreview() {
3422             return mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL
3423                     ? mKeyguardBottomArea.getLeftPreview() : mKeyguardBottomArea.getRightPreview();
3424         }
3425 
3426         @Override
3427         public float getAffordanceFalsingFactor() {
3428             return mStatusBar.isWakeUpComingFromTouch() ? 1.5f : 1.0f;
3429         }
3430 
3431         @Override
3432         public boolean needsAntiFalsing() {
3433             return mBarState == StatusBarState.KEYGUARD;
3434         }
3435     }
3436 
3437     private class OnEmptySpaceClickListener implements
3438             NotificationStackScrollLayout.OnEmptySpaceClickListener {
3439         @Override
3440         public void onEmptySpaceClicked(float x, float y) {
3441             onEmptySpaceClick(x);
3442         }
3443     }
3444 
3445     private class MyOnHeadsUpChangedListener implements OnHeadsUpChangedListener {
3446         @Override
3447         public void onHeadsUpPinnedModeChanged(final boolean inPinnedMode) {
3448             mNotificationStackScroller.setInHeadsUpPinnedMode(inPinnedMode);
3449             if (inPinnedMode) {
3450                 mHeadsUpExistenceChangedRunnable.run();
3451                 updateNotificationTranslucency();
3452             } else {
3453                 setHeadsUpAnimatingAway(true);
3454                 mNotificationStackScroller.runAfterAnimationFinished(
3455                         mHeadsUpExistenceChangedRunnable);
3456             }
3457             updateGestureExclusionRect();
3458             mHeadsUpPinnedMode = inPinnedMode;
3459             updateHeadsUpVisibility();
3460             updateKeyguardStatusBarForHeadsUp();
3461         }
3462 
3463         @Override
3464         public void onHeadsUpPinned(NotificationEntry entry) {
3465             if (!isOnKeyguard()) {
3466                 mNotificationStackScroller.generateHeadsUpAnimation(entry.getHeadsUpAnimationView(),
3467                         true);
3468             }
3469         }
3470 
3471         @Override
3472         public void onHeadsUpUnPinned(NotificationEntry entry) {
3473 
3474             // When we're unpinning the notification via active edge they remain heads-upped,
3475             // we need to make sure that an animation happens in this case, otherwise the
3476             // notification
3477             // will stick to the top without any interaction.
3478             if (isFullyCollapsed() && entry.isRowHeadsUp() && !isOnKeyguard()) {
3479                 mNotificationStackScroller.generateHeadsUpAnimation(
3480                         entry.getHeadsUpAnimationView(), false);
3481                 entry.setHeadsUpIsVisible();
3482             }
3483         }
3484 
3485         @Override
3486         public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) {
3487             mNotificationStackScroller.generateHeadsUpAnimation(entry, isHeadsUp);
3488         }
3489     }
3490 
3491     private class HeightListener implements QS.HeightListener {
3492         public void onQsHeightChanged() {
3493             mQsMaxExpansionHeight = mQs != null ? mQs.getDesiredHeight() : 0;
3494             if (mQsExpanded && mQsFullyExpanded) {
3495                 mQsExpansionHeight = mQsMaxExpansionHeight;
3496                 requestScrollerTopPaddingUpdate(false /* animate */);
3497                 requestPanelHeightUpdate();
3498             }
3499             if (mAccessibilityManager.isEnabled()) {
3500                 mView.setAccessibilityPaneTitle(determineAccessibilityPaneTitle());
3501             }
3502             mNotificationStackScroller.setMaxTopPadding(
3503                     mQsMaxExpansionHeight + mQsNotificationTopPadding);
3504         }
3505     }
3506 
3507     private class ZenModeControllerCallback implements ZenModeController.Callback {
3508         @Override
3509         public void onZenChanged(int zen) {
3510             updateShowEmptyShadeView();
3511         }
3512     }
3513 
3514     private class ConfigurationListener implements ConfigurationController.ConfigurationListener {
3515         @Override
3516         public void onDensityOrFontScaleChanged() {
3517             updateShowEmptyShadeView();
3518         }
3519 
3520         @Override
3521         public void onThemeChanged() {
3522             final int themeResId = mView.getContext().getThemeResId();
3523             if (mThemeResId == themeResId) {
3524                 return;
3525             }
3526             mThemeResId = themeResId;
3527 
3528             reInflateViews();
3529         }
3530 
3531         @Override
3532         public void onOverlayChanged() {
3533             reInflateViews();
3534         }
3535 
3536         @Override
3537         public void onUiModeChanged() {}
3538     }
3539 
3540     private class StatusBarStateListener implements StateListener {
3541         @Override
3542         public void onStateChanged(int statusBarState) {
3543             boolean goingToFullShade = mStatusBarStateController.goingToFullShade();
3544             boolean keyguardFadingAway = mKeyguardStateController.isKeyguardFadingAway();
3545             int oldState = mBarState;
3546             boolean keyguardShowing = statusBarState == StatusBarState.KEYGUARD;
3547             setKeyguardStatusViewVisibility(statusBarState, keyguardFadingAway, goingToFullShade);
3548             setKeyguardBottomAreaVisibility(statusBarState, goingToFullShade);
3549 
3550             mBarState = statusBarState;
3551             mKeyguardShowing = keyguardShowing;
3552 
3553             if (oldState == StatusBarState.KEYGUARD && (goingToFullShade
3554                     || statusBarState == StatusBarState.SHADE_LOCKED)) {
3555                 animateKeyguardStatusBarOut();
3556                 long
3557                         delay =
3558                         mBarState == StatusBarState.SHADE_LOCKED ? 0
3559                                 : mKeyguardStateController.calculateGoingToFullShadeDelay();
3560                 mQs.animateHeaderSlidingIn(delay);
3561             } else if (oldState == StatusBarState.SHADE_LOCKED
3562                     && statusBarState == StatusBarState.KEYGUARD) {
3563                 animateKeyguardStatusBarIn(StackStateAnimator.ANIMATION_DURATION_STANDARD);
3564                 mNotificationStackScroller.resetScrollPosition();
3565                 // Only animate header if the header is visible. If not, it will partially
3566                 // animate out
3567                 // the top of QS
3568                 if (!mQsExpanded) {
3569                     mQs.animateHeaderSlidingOut();
3570                 }
3571             } else {
3572                 mKeyguardStatusBar.setAlpha(1f);
3573                 mKeyguardStatusBar.setVisibility(keyguardShowing ? View.VISIBLE : View.INVISIBLE);
3574                 if (keyguardShowing && oldState != mBarState) {
3575                     if (mQs != null) {
3576                         mQs.hideImmediately();
3577                     }
3578                 }
3579             }
3580             updateKeyguardStatusBarForHeadsUp();
3581             if (keyguardShowing) {
3582                 updateDozingVisibilities(false /* animate */);
3583             }
3584             // THe update needs to happen after the headerSlide in above, otherwise the translation
3585             // would reset
3586             updateQSPulseExpansion();
3587             maybeAnimateBottomAreaAlpha();
3588             resetHorizontalPanelPosition();
3589             updateQsState();
3590         }
3591 
3592         @Override
3593         public void onDozeAmountChanged(float linearAmount, float amount) {
3594             mInterpolatedDarkAmount = amount;
3595             mLinearDarkAmount = linearAmount;
3596             mKeyguardStatusView.setDarkAmount(mInterpolatedDarkAmount);
3597             mKeyguardBottomArea.setDarkAmount(mInterpolatedDarkAmount);
3598             positionClockAndNotifications();
3599         }
3600     }
3601 
3602     private class ExpansionCallback implements PulseExpansionHandler.ExpansionCallback {
3603         public void setEmptyDragAmount(float amount) {
3604             mEmptyDragAmount = amount * 0.2f;
3605             positionClockAndNotifications();
3606         }
3607     }
3608 
3609     private class OnAttachStateChangeListener implements View.OnAttachStateChangeListener {
3610         @Override
3611         public void onViewAttachedToWindow(View v) {
3612             FragmentHostManager.get(mView).addTagListener(QS.TAG, mFragmentListener);
3613             mStatusBarStateController.addCallback(mStatusBarStateListener);
3614             mZenModeController.addCallback(mZenModeControllerCallback);
3615             mConfigurationController.addCallback(mConfigurationListener);
3616             mUpdateMonitor.registerCallback(mKeyguardUpdateCallback);
3617             // Theme might have changed between inflating this view and attaching it to the
3618             // window, so
3619             // force a call to onThemeChanged
3620             mConfigurationListener.onThemeChanged();
3621         }
3622 
3623         @Override
3624         public void onViewDetachedFromWindow(View v) {
3625             FragmentHostManager.get(mView).removeTagListener(QS.TAG, mFragmentListener);
3626             mStatusBarStateController.removeCallback(mStatusBarStateListener);
3627             mZenModeController.removeCallback(mZenModeControllerCallback);
3628             mConfigurationController.removeCallback(mConfigurationListener);
3629             mUpdateMonitor.removeCallback(mKeyguardUpdateCallback);
3630         }
3631     }
3632 
3633     private class OnLayoutChangeListener extends PanelViewController.OnLayoutChangeListener {
3634 
3635         @Override
3636         public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft,
3637                 int oldTop, int oldRight, int oldBottom) {
3638             DejankUtils.startDetectingBlockingIpcs("NVP#onLayout");
3639             super.onLayoutChange(v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom);
3640             setIsFullWidth(mNotificationStackScroller.getWidth() == mView.getWidth());
3641 
3642             // Update Clock Pivot
3643             mKeyguardStatusView.setPivotX(mView.getWidth() / 2);
3644             mKeyguardStatusView.setPivotY(
3645                     (FONT_HEIGHT - CAP_HEIGHT) / 2048f * mKeyguardStatusView.getClockTextSize());
3646 
3647             // Calculate quick setting heights.
3648             int oldMaxHeight = mQsMaxExpansionHeight;
3649             if (mQs != null) {
3650                 float previousMin = mQsMinExpansionHeight;
3651                 mQsMinExpansionHeight = mKeyguardShowing ? 0 : mQs.getQsMinExpansionHeight();
3652                 if (mQsExpansionHeight == previousMin) {
3653                     mQsExpansionHeight = mQsMinExpansionHeight;
3654                 }
3655                 mQsMaxExpansionHeight = mQs.getDesiredHeight();
3656                 mNotificationStackScroller.setMaxTopPadding(
3657                         mQsMaxExpansionHeight + mQsNotificationTopPadding);
3658             }
3659             positionClockAndNotifications();
3660             if (mQsExpanded && mQsFullyExpanded) {
3661                 mQsExpansionHeight = mQsMaxExpansionHeight;
3662                 requestScrollerTopPaddingUpdate(false /* animate */);
3663                 requestPanelHeightUpdate();
3664 
3665                 // Size has changed, start an animation.
3666                 if (mQsMaxExpansionHeight != oldMaxHeight) {
3667                     startQsSizeChangeAnimation(oldMaxHeight, mQsMaxExpansionHeight);
3668                 }
3669             } else if (!mQsExpanded) {
3670                 setQsExpansion(mQsMinExpansionHeight + mLastOverscroll);
3671             }
3672             updateExpandedHeight(getExpandedHeight());
3673             updateHeader();
3674 
3675             // If we are running a size change animation, the animation takes care of the height of
3676             // the container. However, if we are not animating, we always need to make the QS
3677             // container
3678             // the desired height so when closing the QS detail, it stays smaller after the size
3679             // change
3680             // animation is finished but the detail view is still being animated away (this
3681             // animation
3682             // takes longer than the size change animation).
3683             if (mQsSizeChangeAnimator == null && mQs != null) {
3684                 mQs.setHeightOverride(mQs.getDesiredHeight());
3685             }
3686             updateMaxHeadsUpTranslation();
3687             updateGestureExclusionRect();
3688             if (mExpandAfterLayoutRunnable != null) {
3689                 mExpandAfterLayoutRunnable.run();
3690                 mExpandAfterLayoutRunnable = null;
3691             }
3692             DejankUtils.stopDetectingBlockingIpcs("NVP#onLayout");
3693         }
3694     }
3695 
3696     private class DebugDrawable extends Drawable {
3697 
3698         @Override
3699         public void draw(Canvas canvas) {
3700             Paint p = new Paint();
3701             p.setColor(Color.RED);
3702             p.setStrokeWidth(2);
3703             p.setStyle(Paint.Style.STROKE);
3704             canvas.drawLine(0, getMaxPanelHeight(), mView.getWidth(), getMaxPanelHeight(), p);
3705             p.setColor(Color.BLUE);
3706             canvas.drawLine(0, getExpandedHeight(), mView.getWidth(), getExpandedHeight(), p);
3707             p.setColor(Color.GREEN);
3708             canvas.drawLine(0, calculatePanelHeightQsExpanded(), mView.getWidth(),
3709                     calculatePanelHeightQsExpanded(), p);
3710             p.setColor(Color.YELLOW);
3711             canvas.drawLine(0, calculatePanelHeightShade(), mView.getWidth(),
3712                     calculatePanelHeightShade(), p);
3713             p.setColor(Color.MAGENTA);
3714             canvas.drawLine(
3715                     0, calculateQsTopPadding(), mView.getWidth(), calculateQsTopPadding(), p);
3716             p.setColor(Color.CYAN);
3717             canvas.drawLine(0, mClockPositionResult.stackScrollerPadding, mView.getWidth(),
3718                     mNotificationStackScroller.getTopPadding(), p);
3719             p.setColor(Color.GRAY);
3720             canvas.drawLine(0, mClockPositionResult.clockY, mView.getWidth(),
3721                     mClockPositionResult.clockY, p);
3722         }
3723 
3724         @Override
3725         public void setAlpha(int alpha) {
3726 
3727         }
3728 
3729         @Override
3730         public void setColorFilter(ColorFilter colorFilter) {
3731 
3732         }
3733 
3734         @Override
3735         public int getOpacity() {
3736             return 0;
3737         }
3738     }
3739 
3740     private class OnConfigurationChangedListener extends
3741             PanelViewController.OnConfigurationChangedListener {
3742         @Override
3743         public void onConfigurationChanged(Configuration newConfig) {
3744             super.onConfigurationChanged(newConfig);
3745             mAffordanceHelper.onConfigurationChanged();
3746             if (newConfig.orientation != mLastOrientation) {
3747                 resetHorizontalPanelPosition();
3748             }
3749             mLastOrientation = newConfig.orientation;
3750         }
3751     }
3752 
3753     private class OnApplyWindowInsetsListener implements View.OnApplyWindowInsetsListener {
3754         public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) {
3755             mNavigationBarBottomHeight = insets.getStableInsetBottom();
3756             updateMaxHeadsUpTranslation();
3757             return insets;
3758         }
3759     }
3760 }
3761