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