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