• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.systemui.statusbar.phone;
18 
19 import static android.view.View.GONE;
20 
21 import static androidx.constraintlayout.widget.ConstraintSet.END;
22 import static androidx.constraintlayout.widget.ConstraintSet.PARENT_ID;
23 import static androidx.constraintlayout.widget.ConstraintSet.START;
24 
25 import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE;
26 import static com.android.systemui.classifier.Classifier.QS_COLLAPSE;
27 import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
28 import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
29 import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL;
30 
31 import static java.lang.Float.isNaN;
32 
33 import android.animation.Animator;
34 import android.animation.AnimatorListenerAdapter;
35 import android.animation.ValueAnimator;
36 import android.app.ActivityManager;
37 import android.app.Fragment;
38 import android.app.StatusBarManager;
39 import android.content.ContentResolver;
40 import android.content.pm.ResolveInfo;
41 import android.content.res.Configuration;
42 import android.content.res.Resources;
43 import android.database.ContentObserver;
44 import android.graphics.Canvas;
45 import android.graphics.Color;
46 import android.graphics.ColorFilter;
47 import android.graphics.Insets;
48 import android.graphics.Paint;
49 import android.graphics.PointF;
50 import android.graphics.Rect;
51 import android.graphics.Region;
52 import android.graphics.drawable.Drawable;
53 import android.hardware.biometrics.BiometricSourceType;
54 import android.os.Bundle;
55 import android.os.Handler;
56 import android.os.PowerManager;
57 import android.os.SystemClock;
58 import android.os.UserManager;
59 import android.os.VibrationEffect;
60 import android.provider.Settings;
61 import android.util.Log;
62 import android.util.MathUtils;
63 import android.view.LayoutInflater;
64 import android.view.MotionEvent;
65 import android.view.VelocityTracker;
66 import android.view.View;
67 import android.view.ViewGroup;
68 import android.view.ViewPropertyAnimator;
69 import android.view.ViewStub;
70 import android.view.ViewTreeObserver;
71 import android.view.WindowInsets;
72 import android.view.accessibility.AccessibilityManager;
73 import android.view.accessibility.AccessibilityNodeInfo;
74 import android.widget.FrameLayout;
75 
76 import androidx.constraintlayout.widget.ConstraintSet;
77 
78 import com.android.internal.annotations.VisibleForTesting;
79 import com.android.internal.jank.InteractionJankMonitor;
80 import com.android.internal.logging.MetricsLogger;
81 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
82 import com.android.internal.policy.ScreenDecorationsUtils;
83 import com.android.internal.util.LatencyTracker;
84 import com.android.keyguard.KeyguardStatusView;
85 import com.android.keyguard.KeyguardStatusViewController;
86 import com.android.keyguard.KeyguardUpdateMonitor;
87 import com.android.keyguard.KeyguardUpdateMonitorCallback;
88 import com.android.keyguard.LockIconViewController;
89 import com.android.keyguard.dagger.KeyguardQsUserSwitchComponent;
90 import com.android.keyguard.dagger.KeyguardStatusBarViewComponent;
91 import com.android.keyguard.dagger.KeyguardStatusViewComponent;
92 import com.android.keyguard.dagger.KeyguardUserSwitcherComponent;
93 import com.android.systemui.DejankUtils;
94 import com.android.systemui.R;
95 import com.android.systemui.animation.ActivityLaunchAnimator;
96 import com.android.systemui.animation.Interpolators;
97 import com.android.systemui.biometrics.AuthController;
98 import com.android.systemui.classifier.Classifier;
99 import com.android.systemui.classifier.FalsingCollector;
100 import com.android.systemui.controls.dagger.ControlsComponent;
101 import com.android.systemui.dagger.qualifiers.DisplayId;
102 import com.android.systemui.dagger.qualifiers.Main;
103 import com.android.systemui.doze.DozeLog;
104 import com.android.systemui.fragments.FragmentHostManager.FragmentListener;
105 import com.android.systemui.fragments.FragmentService;
106 import com.android.systemui.media.KeyguardMediaController;
107 import com.android.systemui.media.MediaDataManager;
108 import com.android.systemui.media.MediaHierarchyManager;
109 import com.android.systemui.navigationbar.NavigationModeController;
110 import com.android.systemui.plugins.FalsingManager;
111 import com.android.systemui.plugins.FalsingManager.FalsingTapListener;
112 import com.android.systemui.plugins.qs.DetailAdapter;
113 import com.android.systemui.plugins.qs.QS;
114 import com.android.systemui.plugins.statusbar.StatusBarStateController;
115 import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
116 import com.android.systemui.qs.QSDetailDisplayer;
117 import com.android.systemui.screenrecord.RecordingController;
118 import com.android.systemui.shared.system.QuickStepContract;
119 import com.android.systemui.statusbar.CommandQueue;
120 import com.android.systemui.statusbar.FeatureFlags;
121 import com.android.systemui.statusbar.GestureRecorder;
122 import com.android.systemui.statusbar.KeyguardAffordanceView;
123 import com.android.systemui.statusbar.KeyguardIndicationController;
124 import com.android.systemui.statusbar.LockscreenShadeTransitionController;
125 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
126 import com.android.systemui.statusbar.NotificationRemoteInputManager;
127 import com.android.systemui.statusbar.NotificationShadeDepthController;
128 import com.android.systemui.statusbar.NotificationShelfController;
129 import com.android.systemui.statusbar.PulseExpansionHandler;
130 import com.android.systemui.statusbar.RemoteInputController;
131 import com.android.systemui.statusbar.StatusBarState;
132 import com.android.systemui.statusbar.SysuiStatusBarStateController;
133 import com.android.systemui.statusbar.VibratorHelper;
134 import com.android.systemui.statusbar.events.PrivacyDotViewController;
135 import com.android.systemui.statusbar.notification.AnimatableProperty;
136 import com.android.systemui.statusbar.notification.ConversationNotificationManager;
137 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
138 import com.android.systemui.statusbar.notification.NotificationEntryManager;
139 import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
140 import com.android.systemui.statusbar.notification.PropertyAnimator;
141 import com.android.systemui.statusbar.notification.ViewGroupFadeHelper;
142 import com.android.systemui.statusbar.notification.collection.ListEntry;
143 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
144 import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
145 import com.android.systemui.statusbar.notification.collection.render.ShadeViewManager;
146 import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
147 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
148 import com.android.systemui.statusbar.notification.row.ExpandableView;
149 import com.android.systemui.statusbar.notification.stack.AmbientState;
150 import com.android.systemui.statusbar.notification.stack.AnimationProperties;
151 import com.android.systemui.statusbar.notification.stack.MediaHeaderView;
152 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
153 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
154 import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
155 import com.android.systemui.statusbar.phone.LockscreenGestureLogger.LockscreenUiEvent;
156 import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
157 import com.android.systemui.statusbar.policy.ConfigurationController;
158 import com.android.systemui.statusbar.policy.KeyguardQsUserSwitchController;
159 import com.android.systemui.statusbar.policy.KeyguardStateController;
160 import com.android.systemui.statusbar.policy.KeyguardUserSwitcherController;
161 import com.android.systemui.statusbar.policy.KeyguardUserSwitcherView;
162 import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
163 import com.android.systemui.util.Utils;
164 import com.android.systemui.util.settings.SecureSettings;
165 import com.android.systemui.wallet.controller.QuickAccessWalletController;
166 import com.android.wm.shell.animation.FlingAnimationUtils;
167 
168 import java.io.FileDescriptor;
169 import java.io.PrintWriter;
170 import java.util.ArrayList;
171 import java.util.Collections;
172 import java.util.List;
173 import java.util.concurrent.Executor;
174 import java.util.function.Consumer;
175 import java.util.function.Function;
176 
177 import javax.inject.Inject;
178 import javax.inject.Provider;
179 
180 @StatusBarComponent.StatusBarScope
181 public class NotificationPanelViewController extends PanelViewController {
182 
183     private static final boolean DEBUG = false;
184 
185     /**
186      * The parallax amount of the quick settings translation when dragging down the panel
187      */
188     private static final float QS_PARALLAX_AMOUNT = 0.175f;
189 
190     /**
191      * Fling expanding QS.
192      */
193     private static final int FLING_EXPAND = 0;
194 
195     /**
196      * Fling collapsing QS, potentially stopping when QS becomes QQS.
197      */
198     private static final int FLING_COLLAPSE = 1;
199 
200     /**
201      * Fling until QS is completely hidden.
202      */
203     private static final int FLING_HIDE = 2;
204     private static final long ANIMATION_DELAY_ICON_FADE_IN =
205             ActivityLaunchAnimator.ANIMATION_DURATION - CollapsedStatusBarFragment.FADE_IN_DURATION
206                     - CollapsedStatusBarFragment.FADE_IN_DELAY - 48;
207 
208     private final DozeParameters mDozeParameters;
209     private final OnHeightChangedListener mOnHeightChangedListener = new OnHeightChangedListener();
210     private final OnClickListener mOnClickListener = new OnClickListener();
211     private final OnOverscrollTopChangedListener
212             mOnOverscrollTopChangedListener =
213             new OnOverscrollTopChangedListener();
214     private final KeyguardAffordanceHelperCallback
215             mKeyguardAffordanceHelperCallback =
216             new KeyguardAffordanceHelperCallback();
217     private final OnEmptySpaceClickListener
218             mOnEmptySpaceClickListener =
219             new OnEmptySpaceClickListener();
220     private final MyOnHeadsUpChangedListener
221             mOnHeadsUpChangedListener =
222             new MyOnHeadsUpChangedListener();
223     private final HeightListener mHeightListener = new HeightListener();
224     private final ConfigurationListener mConfigurationListener = new ConfigurationListener();
225     private final SettingsChangeObserver mSettingsChangeObserver;
226 
227     @VisibleForTesting final StatusBarStateListener mStatusBarStateListener =
228             new StatusBarStateListener();
229     private final BiometricUnlockController mBiometricUnlockController;
230     private final NotificationPanelView mView;
231     private final VibratorHelper mVibratorHelper;
232     private final MetricsLogger mMetricsLogger;
233     private final ActivityManager mActivityManager;
234     private final ConfigurationController mConfigurationController;
235     private final Provider<FlingAnimationUtils.Builder> mFlingAnimationUtilsBuilder;
236     private final NotificationStackScrollLayoutController mNotificationStackScrollLayoutController;
237     private final NotificationIconAreaController mNotificationIconAreaController;
238 
239     // Cap and total height of Roboto font. Needs to be adjusted when font for the big clock is
240     // changed.
241     private static final int CAP_HEIGHT = 1456;
242     private static final int FONT_HEIGHT = 2163;
243 
244     /**
245      * Maximum time before which we will expand the panel even for slow motions when getting a
246      * touch passed over from launcher.
247      */
248     private static final int MAX_TIME_TO_OPEN_WHEN_FLINGING_FROM_LAUNCHER = 300;
249 
250     private static final String COUNTER_PANEL_OPEN = "panel_open";
251     private static final String COUNTER_PANEL_OPEN_QS = "panel_open_qs";
252     private static final String COUNTER_PANEL_OPEN_PEEK = "panel_open_peek";
253 
254     private static final Rect M_DUMMY_DIRTY_RECT = new Rect(0, 0, 1, 1);
255     private static final Rect EMPTY_RECT = new Rect();
256 
257     private final AnimatableProperty KEYGUARD_HEADS_UP_SHOWING_AMOUNT = AnimatableProperty.from(
258             "KEYGUARD_HEADS_UP_SHOWING_AMOUNT",
259             (notificationPanelView, aFloat) -> setKeyguardHeadsUpShowingAmount(aFloat),
260             (Function<NotificationPanelView, Float>) notificationPanelView ->
261                     getKeyguardHeadsUpShowingAmount(),
262             R.id.keyguard_hun_animator_tag, R.id.keyguard_hun_animator_end_tag,
263             R.id.keyguard_hun_animator_start_tag);
264     private static final AnimationProperties
265             KEYGUARD_HUN_PROPERTIES =
266             new AnimationProperties().setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
267     @VisibleForTesting
268     final KeyguardUpdateMonitorCallback
269             mKeyguardUpdateCallback =
270             new KeyguardUpdateMonitorCallback() {
271 
272                 @Override
273                 public void onBiometricAuthenticated(int userId,
274                         BiometricSourceType biometricSourceType,
275                         boolean isStrongBiometric) {
276                     if (mFirstBypassAttempt
277                             && mUpdateMonitor.isUnlockingWithBiometricAllowed(isStrongBiometric)) {
278                         mDelayShowingKeyguardStatusBar = true;
279                     }
280                 }
281 
282                 @Override
283                 public void onBiometricRunningStateChanged(boolean running,
284                         BiometricSourceType biometricSourceType) {
285                     boolean
286                             keyguardOrShadeLocked =
287                             mBarState == KEYGUARD
288                                     || mBarState == StatusBarState.SHADE_LOCKED;
289                     if (!running && mFirstBypassAttempt && keyguardOrShadeLocked && !mDozing
290                             && !mDelayShowingKeyguardStatusBar
291                             && !mBiometricUnlockController.isBiometricUnlock()) {
292                         mFirstBypassAttempt = false;
293                         animateKeyguardStatusBarIn(StackStateAnimator.ANIMATION_DURATION_STANDARD);
294                     }
295                 }
296 
297                 @Override
298                 public void onFinishedGoingToSleep(int why) {
299                     mFirstBypassAttempt = mKeyguardBypassController.getBypassEnabled();
300                     mDelayShowingKeyguardStatusBar = false;
301                 }
302     };
303 
304     private final LayoutInflater mLayoutInflater;
305     private final PowerManager mPowerManager;
306     private final AccessibilityManager mAccessibilityManager;
307     private final NotificationWakeUpCoordinator mWakeUpCoordinator;
308     private final PulseExpansionHandler mPulseExpansionHandler;
309     private final KeyguardBypassController mKeyguardBypassController;
310     private final KeyguardUpdateMonitor mUpdateMonitor;
311     private final ConversationNotificationManager mConversationNotificationManager;
312     private final AuthController mAuthController;
313     private final MediaHierarchyManager mMediaHierarchyManager;
314     private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
315     private final KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory;
316     private final KeyguardQsUserSwitchComponent.Factory mKeyguardQsUserSwitchComponentFactory;
317     private final KeyguardUserSwitcherComponent.Factory mKeyguardUserSwitcherComponentFactory;
318     private final KeyguardStatusBarViewComponent.Factory mKeyguardStatusBarViewComponentFactory;
319     private final QSDetailDisplayer mQSDetailDisplayer;
320     private final FragmentService mFragmentService;
321     private final FeatureFlags mFeatureFlags;
322     private final ScrimController mScrimController;
323     private final PrivacyDotViewController mPrivacyDotViewController;
324     private final QuickAccessWalletController mQuickAccessWalletController;
325     private final ControlsComponent mControlsComponent;
326     private final NotificationRemoteInputManager mRemoteInputManager;
327 
328     // Maximum # notifications to show on Keyguard; extras will be collapsed in an overflow card.
329     // If there are exactly 1 + mMaxKeyguardNotifications, then still shows all notifications
330     private final int mMaxKeyguardNotifications;
331     private final LockscreenShadeTransitionController mLockscreenShadeTransitionController;
332     private final TapAgainViewController mTapAgainViewController;
333     private final RecordingController mRecordingController;
334     private boolean mShouldUseSplitNotificationShade;
335     // Current max allowed keyguard notifications determined by measuring the panel
336     private int mMaxAllowedKeyguardNotifications;
337 
338     private ViewGroup mPreviewContainer;
339     private KeyguardAffordanceHelper mAffordanceHelper;
340     private KeyguardQsUserSwitchController mKeyguardQsUserSwitchController;
341     private KeyguardUserSwitcherController mKeyguardUserSwitcherController;
342     private KeyguardStatusBarView mKeyguardStatusBar;
343     private KeyguardStatusBarViewController mKeyguarStatusBarViewController;
344     private ViewGroup mBigClockContainer;
345     @VisibleForTesting QS mQs;
346     private FrameLayout mQsFrame;
347     private KeyguardStatusViewController mKeyguardStatusViewController;
348     private LockIconViewController mLockIconViewController;
349     private NotificationsQuickSettingsContainer mNotificationContainerParent;
350     private boolean mAnimateNextPositionUpdate;
351     private float mQuickQsOffsetHeight;
352     private UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
353 
354     private int mTrackingPointer;
355     private VelocityTracker mQsVelocityTracker;
356     private boolean mQsTracking;
357 
358     /**
359      * If set, the ongoing touch gesture might both trigger the expansion in {@link PanelView} and
360      * the expansion for quick settings.
361      */
362     private boolean mConflictingQsExpansionGesture;
363 
364     private boolean mPanelExpanded;
365     private boolean mQsExpanded;
366     private boolean mQsExpandedWhenExpandingStarted;
367     private boolean mQsFullyExpanded;
368     private boolean mKeyguardShowing;
369     private boolean mKeyguardQsUserSwitchEnabled;
370     private boolean mKeyguardUserSwitcherEnabled;
371     private boolean mDozing;
372     private boolean mDozingOnDown;
373     private int mBarState;
374     private float mInitialHeightOnTouch;
375     private float mInitialTouchX;
376     private float mInitialTouchY;
377     private float mQsExpansionHeight;
378     private int mQsMinExpansionHeight;
379     private int mQsMaxExpansionHeight;
380     private int mQsPeekHeight;
381     private boolean mStackScrollerOverscrolling;
382     private boolean mQsExpansionFromOverscroll;
383     private float mLastOverscroll;
384     private boolean mQsExpansionEnabledPolicy = true;
385     private boolean mQsExpansionEnabledAmbient = true;
386     private ValueAnimator mQsExpansionAnimator;
387     private FlingAnimationUtils mFlingAnimationUtils;
388     private int mStatusBarMinHeight;
389     private int mStatusBarHeaderHeightKeyguard;
390     private int mNotificationsHeaderCollideDistance;
391     private float mOverStretchAmount;
392     private float mDownX;
393     private float mDownY;
394     private int mDisplayTopInset = 0; // in pixels
395     private int mDisplayRightInset = 0; // in pixels
396     private int mSplitShadeNotificationsTopPadding;
397 
398     private final KeyguardClockPositionAlgorithm
399             mClockPositionAlgorithm =
400             new KeyguardClockPositionAlgorithm();
401     private final KeyguardClockPositionAlgorithm.Result
402             mClockPositionResult =
403             new KeyguardClockPositionAlgorithm.Result();
404     private boolean mIsExpanding;
405 
406     private boolean mBlockTouches;
407     // Used for two finger gesture as well as accessibility shortcut to QS.
408     private boolean mQsExpandImmediate;
409     private boolean mTwoFingerQsExpandPossible;
410     private String mHeaderDebugInfo;
411 
412     /**
413      * If we are in a panel collapsing motion, we reset scrollY of our scroll view but still
414      * need to take this into account in our panel height calculation.
415      */
416     private boolean mQsAnimatorExpand;
417     private boolean mIsLaunchTransitionFinished;
418     private boolean mIsLaunchTransitionRunning;
419     private Runnable mLaunchAnimationEndRunnable;
420     private boolean mOnlyAffordanceInThisMotion;
421     private ValueAnimator mQsSizeChangeAnimator;
422 
423     private boolean mQsScrimEnabled = true;
424     private boolean mQsTouchAboveFalsingThreshold;
425     private int mQsFalsingThreshold;
426 
427     private float mKeyguardStatusBarAnimateAlpha = 1f;
428     private HeadsUpTouchHelper mHeadsUpTouchHelper;
429     private boolean mListenForHeadsUp;
430     private int mNavigationBarBottomHeight;
431     private boolean mExpandingFromHeadsUp;
432     private boolean mCollapsedOnDown;
433     private int mPositionMinSideMargin;
434     private int mLastOrientation = -1;
435     private boolean mClosingWithAlphaFadeOut;
436     private boolean mHeadsUpAnimatingAway;
437     private boolean mLaunchingAffordance;
438     private boolean mAffordanceHasPreview;
439     private final FalsingManager mFalsingManager;
440     private final FalsingCollector mFalsingCollector;
441     private String mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_AFFORDANCE;
442 
443     private Runnable mHeadsUpExistenceChangedRunnable = () -> {
444         setHeadsUpAnimatingAway(false);
445         notifyBarPanelExpansionChanged();
446     };
447     // TODO (b/162832756): once migrated to the new pipeline, delete legacy group manager
448     private NotificationGroupManagerLegacy mGroupManager;
449     private boolean mShowIconsWhenExpanded;
450     private int mIndicationBottomPadding;
451     private int mAmbientIndicationBottomPadding;
452     private boolean mIsFullWidth;
453     private boolean mBlockingExpansionForCurrentTouch;
454 
455     /**
456      * Following variables maintain state of events when input focus transfer may occur.
457      */
458     private boolean mExpectingSynthesizedDown; // expecting to see synthesized DOWN event
459     private boolean mLastEventSynthesizedDown; // last event was synthesized DOWN event
460 
461     /**
462      * Current dark amount that follows regular interpolation curve of animation.
463      */
464     private float mInterpolatedDarkAmount;
465 
466     /**
467      * Dark amount that animates from 0 to 1 or vice-versa in linear manner, even if the
468      * interpolation curve is different.
469      */
470     private float mLinearDarkAmount;
471 
472     private boolean mPulsing;
473     private LockscreenGestureLogger mLockscreenGestureLogger = new LockscreenGestureLogger();
474     private boolean mUserSetupComplete;
475     private int mQsNotificationTopPadding;
476     private boolean mHideIconsDuringLaunchAnimation = true;
477     private int mStackScrollerMeasuringPass;
478     private ArrayList<Consumer<ExpandableNotificationRow>>
479             mTrackingHeadsUpListeners =
480             new ArrayList<>();
481     private Runnable mVerticalTranslationListener;
482     private HeadsUpAppearanceController mHeadsUpAppearanceController;
483 
484     private int mPanelAlpha;
485     private Runnable mPanelAlphaEndAction;
486     private float mBottomAreaShadeAlpha;
487     private final ValueAnimator mBottomAreaShadeAlphaAnimator;
488     private final AnimatableProperty mPanelAlphaAnimator = AnimatableProperty.from("panelAlpha",
489             NotificationPanelView::setPanelAlphaInternal,
490             NotificationPanelView::getCurrentPanelAlpha,
491             R.id.panel_alpha_animator_tag, R.id.panel_alpha_animator_start_tag,
492             R.id.panel_alpha_animator_end_tag);
493     private final AnimationProperties mPanelAlphaOutPropertiesAnimator =
494             new AnimationProperties().setDuration(150).setCustomInterpolator(
495                     mPanelAlphaAnimator.getProperty(), Interpolators.ALPHA_OUT);
496     private final AnimationProperties mPanelAlphaInPropertiesAnimator =
497             new AnimationProperties().setDuration(200).setAnimationEndAction((property) -> {
498                             if (mPanelAlphaEndAction != null) {
499                                 mPanelAlphaEndAction.run();
500                             }
501                         }).setCustomInterpolator(
502                     mPanelAlphaAnimator.getProperty(), Interpolators.ALPHA_IN);
503     private final NotificationEntryManager mEntryManager;
504 
505     private final CommandQueue mCommandQueue;
506     private final NotificationLockscreenUserManager mLockscreenUserManager;
507     private final UserManager mUserManager;
508     private final MediaDataManager mMediaDataManager;
509     private NotificationShadeDepthController mDepthController;
510     private int mDisplayId;
511 
512     /**
513      * Cache the resource id of the theme to avoid unnecessary work in onThemeChanged.
514      *
515      * onThemeChanged is forced when the theme might not have changed. So, to avoid unncessary
516      * work, check the current id with the cached id.
517      */
518     private int mThemeResId;
519     private KeyguardIndicationController mKeyguardIndicationController;
520     private int mShelfHeight;
521     private int mDarkIconSize;
522     private int mHeadsUpInset;
523     private boolean mHeadsUpPinnedMode;
524     private float mKeyguardHeadsUpShowingAmount = 0.0f;
525     private boolean mShowingKeyguardHeadsUp;
526     private boolean mAllowExpandForSmallExpansion;
527     private Runnable mExpandAfterLayoutRunnable;
528 
529     /**
530      * The padding between the start of notifications and the qs boundary on the lockscreen.
531      * On lockscreen, notifications aren't inset this extra amount, but we still want the
532      * qs boundary to be padded.
533      */
534     private int mLockscreenNotificationQSPadding;
535 
536     /**
537      * The amount of progress we are currently in if we're transitioning to the full shade.
538      * 0.0f means we're not transitioning yet, while 1 means we're all the way in the full
539      * shade. This value can also go beyond 1.1 when we're overshooting!
540      */
541     private float mTransitioningToFullShadeProgress;
542 
543     /**
544      * Position of the qs bottom during the full shade transition. This is needed as the toppadding
545      * can change during state changes, which makes it much harder to do animations
546      */
547     private int mTransitionToFullShadeQSPosition;
548 
549     /**
550      * Distance that the full shade transition takes in order for qs to fully transition to the
551      * shade.
552      */
553     private int mDistanceForQSFullShadeTransition;
554 
555     /**
556      * The translation amount for QS for the full shade transition
557      */
558     private float mQsTranslationForFullShadeTransition;
559 
560     /**
561      * The maximum overshoot allowed for the top padding for the full shade transition
562      */
563     private int mMaxOverscrollAmountForPulse;
564 
565     /**
566      * Should we animate the next bounds update
567      */
568     private boolean mAnimateNextNotificationBounds;
569     /**
570      * The delay for the next bounds animation
571      */
572     private long mNotificationBoundsAnimationDelay;
573 
574     /**
575      * The duration of the notification bounds animation
576      */
577     private long mNotificationBoundsAnimationDuration;
578 
579     /**
580      * Is this a collapse that started on the panel where we should allow the panel to intercept
581      */
582     private boolean mIsPanelCollapseOnQQS;
583 
584     /**
585      * If face auth with bypass is running for the first time after you turn on the screen.
586      * (From aod or screen off)
587      */
588     private boolean mFirstBypassAttempt;
589     /**
590      * If auth happens successfully during {@code mFirstBypassAttempt}, and we should wait until
591      * the keyguard is dismissed to show the status bar.
592      */
593     private boolean mDelayShowingKeyguardStatusBar;
594 
595     private boolean mAnimatingQS;
596 
597     /**
598      * The end bounds of a clipping animation.
599      */
600     private final Rect mQsClippingAnimationEndBounds = new Rect();
601 
602     /**
603      * The animator for the qs clipping bounds.
604      */
605     private ValueAnimator mQsClippingAnimation = null;
606 
607     /**
608      * Is the current animator resetting the qs translation.
609      */
610     private boolean mIsQsTranslationResetAnimator;
611 
612     /**
613      * Is the current animator resetting the pulse expansion after a drag down
614      */
615     private boolean mIsPulseExpansionResetAnimator;
616     private final Rect mKeyguardStatusAreaClipBounds = new Rect();
617     private final Region mQsInterceptRegion = new Region();
618 
619     /**
620      * The alpha of the views which only show on the keyguard but not in shade / shade locked
621      */
622     private float mKeyguardOnlyContentAlpha = 1.0f;
623 
624     /**
625      * Are we currently in gesture navigation
626      */
627     private boolean mIsGestureNavigation;
628     private int mOldLayoutDirection;
629     private NotificationShelfController mNotificationShelfController;
630     private int mScrimCornerRadius;
631     private int mScreenCornerRadius;
632     private boolean mQSAnimatingHiddenFromCollapsed;
633 
634     private int mQsClipTop;
635     private int mQsClipBottom;
636     private boolean mQsVisible;
637     private final ContentResolver mContentResolver;
638 
639     private final Executor mUiExecutor;
640     private final SecureSettings mSecureSettings;
641 
642     private KeyguardMediaController mKeyguardMediaController;
643 
644     private View.AccessibilityDelegate mAccessibilityDelegate = new View.AccessibilityDelegate() {
645         @Override
646         public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
647             super.onInitializeAccessibilityNodeInfo(host, info);
648             info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD);
649             info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_UP);
650         }
651 
652         @Override
653         public boolean performAccessibilityAction(View host, int action, Bundle args) {
654             if (action == AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD.getId()
655                     || action
656                     == AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_UP.getId()) {
657                 mStatusBarKeyguardViewManager.showBouncer(true);
658                 return true;
659             }
660             return super.performAccessibilityAction(host, action, args);
661         }
662     };
663 
664     private final FalsingTapListener mFalsingTapListener = new FalsingTapListener() {
665         @Override
666         public void onDoubleTapRequired() {
667             if (mStatusBarStateController.getState() == StatusBarState.SHADE_LOCKED) {
668                 mTapAgainViewController.show();
669             } else {
670                 mKeyguardIndicationController.showTransientIndication(
671                         R.string.notification_tap_again);
672             }
673             mVibratorHelper.vibrate(VibrationEffect.EFFECT_STRENGTH_MEDIUM);
674         }
675     };
676 
677     @Inject
NotificationPanelViewController(NotificationPanelView view, @Main Resources resources, @Main Handler handler, LayoutInflater layoutInflater, NotificationWakeUpCoordinator coordinator, PulseExpansionHandler pulseExpansionHandler, DynamicPrivacyController dynamicPrivacyController, KeyguardBypassController bypassController, FalsingManager falsingManager, FalsingCollector falsingCollector, NotificationLockscreenUserManager notificationLockscreenUserManager, NotificationEntryManager notificationEntryManager, KeyguardStateController keyguardStateController, StatusBarStateController statusBarStateController, DozeLog dozeLog, DozeParameters dozeParameters, CommandQueue commandQueue, VibratorHelper vibratorHelper, LatencyTracker latencyTracker, PowerManager powerManager, AccessibilityManager accessibilityManager, @DisplayId int displayId, KeyguardUpdateMonitor keyguardUpdateMonitor, MetricsLogger metricsLogger, ActivityManager activityManager, ConfigurationController configurationController, Provider<FlingAnimationUtils.Builder> flingAnimationUtilsBuilder, StatusBarTouchableRegionManager statusBarTouchableRegionManager, ConversationNotificationManager conversationNotificationManager, MediaHierarchyManager mediaHierarchyManager, BiometricUnlockController biometricUnlockController, StatusBarKeyguardViewManager statusBarKeyguardViewManager, NotificationStackScrollLayoutController notificationStackScrollLayoutController, KeyguardStatusViewComponent.Factory keyguardStatusViewComponentFactory, KeyguardQsUserSwitchComponent.Factory keyguardQsUserSwitchComponentFactory, KeyguardUserSwitcherComponent.Factory keyguardUserSwitcherComponentFactory, KeyguardStatusBarViewComponent.Factory keyguardStatusBarViewComponentFactory, LockscreenShadeTransitionController lockscreenShadeTransitionController, QSDetailDisplayer qsDetailDisplayer, NotificationGroupManagerLegacy groupManager, NotificationIconAreaController notificationIconAreaController, AuthController authController, ScrimController scrimController, UserManager userManager, MediaDataManager mediaDataManager, NotificationShadeDepthController notificationShadeDepthController, AmbientState ambientState, LockIconViewController lockIconViewController, FeatureFlags featureFlags, KeyguardMediaController keyguardMediaController, PrivacyDotViewController privacyDotViewController, TapAgainViewController tapAgainViewController, NavigationModeController navigationModeController, FragmentService fragmentService, ContentResolver contentResolver, QuickAccessWalletController quickAccessWalletController, RecordingController recordingController, @Main Executor uiExecutor, SecureSettings secureSettings, UnlockedScreenOffAnimationController unlockedScreenOffAnimationController, NotificationRemoteInputManager remoteInputManager, ControlsComponent controlsComponent)678     public NotificationPanelViewController(NotificationPanelView view,
679             @Main Resources resources,
680             @Main Handler handler,
681             LayoutInflater layoutInflater,
682             NotificationWakeUpCoordinator coordinator, PulseExpansionHandler pulseExpansionHandler,
683             DynamicPrivacyController dynamicPrivacyController,
684             KeyguardBypassController bypassController, FalsingManager falsingManager,
685             FalsingCollector falsingCollector,
686             NotificationLockscreenUserManager notificationLockscreenUserManager,
687             NotificationEntryManager notificationEntryManager,
688             KeyguardStateController keyguardStateController,
689             StatusBarStateController statusBarStateController, DozeLog dozeLog,
690             DozeParameters dozeParameters, CommandQueue commandQueue, VibratorHelper vibratorHelper,
691             LatencyTracker latencyTracker, PowerManager powerManager,
692             AccessibilityManager accessibilityManager, @DisplayId int displayId,
693             KeyguardUpdateMonitor keyguardUpdateMonitor, MetricsLogger metricsLogger,
694             ActivityManager activityManager,
695             ConfigurationController configurationController,
696             Provider<FlingAnimationUtils.Builder> flingAnimationUtilsBuilder,
697             StatusBarTouchableRegionManager statusBarTouchableRegionManager,
698             ConversationNotificationManager conversationNotificationManager,
699             MediaHierarchyManager mediaHierarchyManager,
700             BiometricUnlockController biometricUnlockController,
701             StatusBarKeyguardViewManager statusBarKeyguardViewManager,
702             NotificationStackScrollLayoutController notificationStackScrollLayoutController,
703             KeyguardStatusViewComponent.Factory keyguardStatusViewComponentFactory,
704             KeyguardQsUserSwitchComponent.Factory keyguardQsUserSwitchComponentFactory,
705             KeyguardUserSwitcherComponent.Factory keyguardUserSwitcherComponentFactory,
706             KeyguardStatusBarViewComponent.Factory keyguardStatusBarViewComponentFactory,
707             LockscreenShadeTransitionController lockscreenShadeTransitionController,
708             QSDetailDisplayer qsDetailDisplayer,
709             NotificationGroupManagerLegacy groupManager,
710             NotificationIconAreaController notificationIconAreaController,
711             AuthController authController,
712             ScrimController scrimController,
713             UserManager userManager,
714             MediaDataManager mediaDataManager,
715             NotificationShadeDepthController notificationShadeDepthController,
716             AmbientState ambientState,
717             LockIconViewController lockIconViewController,
718             FeatureFlags featureFlags,
719             KeyguardMediaController keyguardMediaController,
720             PrivacyDotViewController privacyDotViewController,
721             TapAgainViewController tapAgainViewController,
722             NavigationModeController navigationModeController,
723             FragmentService fragmentService,
724             ContentResolver contentResolver,
725             QuickAccessWalletController quickAccessWalletController,
726             RecordingController recordingController,
727             @Main Executor uiExecutor,
728             SecureSettings secureSettings,
729             UnlockedScreenOffAnimationController unlockedScreenOffAnimationController,
730             NotificationRemoteInputManager remoteInputManager,
731             ControlsComponent controlsComponent) {
732         super(view, falsingManager, dozeLog, keyguardStateController,
733                 (SysuiStatusBarStateController) statusBarStateController, vibratorHelper,
734                 statusBarKeyguardViewManager, latencyTracker, flingAnimationUtilsBuilder.get(),
735                 statusBarTouchableRegionManager, ambientState);
736         mView = view;
737         mVibratorHelper = vibratorHelper;
738         mKeyguardMediaController = keyguardMediaController;
739         mPrivacyDotViewController = privacyDotViewController;
740         mQuickAccessWalletController = quickAccessWalletController;
741         mControlsComponent = controlsComponent;
742         mMetricsLogger = metricsLogger;
743         mActivityManager = activityManager;
744         mConfigurationController = configurationController;
745         mFlingAnimationUtilsBuilder = flingAnimationUtilsBuilder;
746         mMediaHierarchyManager = mediaHierarchyManager;
747         mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
748         mNotificationStackScrollLayoutController = notificationStackScrollLayoutController;
749         mGroupManager = groupManager;
750         mNotificationIconAreaController = notificationIconAreaController;
751         mKeyguardStatusViewComponentFactory = keyguardStatusViewComponentFactory;
752         mKeyguardStatusBarViewComponentFactory = keyguardStatusBarViewComponentFactory;
753         mDepthController = notificationShadeDepthController;
754         mFeatureFlags = featureFlags;
755         mContentResolver = contentResolver;
756         mKeyguardQsUserSwitchComponentFactory = keyguardQsUserSwitchComponentFactory;
757         mKeyguardUserSwitcherComponentFactory = keyguardUserSwitcherComponentFactory;
758         mQSDetailDisplayer = qsDetailDisplayer;
759         mFragmentService = fragmentService;
760         mSettingsChangeObserver = new SettingsChangeObserver(handler);
761         mShouldUseSplitNotificationShade =
762                 Utils.shouldUseSplitNotificationShade(mFeatureFlags, mResources);
763         mView.setWillNotDraw(!DEBUG);
764         mLayoutInflater = layoutInflater;
765         mFalsingManager = falsingManager;
766         mFalsingCollector = falsingCollector;
767         mPowerManager = powerManager;
768         mWakeUpCoordinator = coordinator;
769         mAccessibilityManager = accessibilityManager;
770         mView.setAccessibilityPaneTitle(determineAccessibilityPaneTitle());
771         setPanelAlpha(255, false /* animate */);
772         mCommandQueue = commandQueue;
773         mRecordingController = recordingController;
774         mDisplayId = displayId;
775         mPulseExpansionHandler = pulseExpansionHandler;
776         mDozeParameters = dozeParameters;
777         mBiometricUnlockController = biometricUnlockController;
778         mScrimController = scrimController;
779         mScrimController.setClipsQsScrim(!mShouldUseSplitNotificationShade);
780         mUserManager = userManager;
781         mMediaDataManager = mediaDataManager;
782         mTapAgainViewController = tapAgainViewController;
783         mUiExecutor = uiExecutor;
784         mSecureSettings = secureSettings;
785         pulseExpansionHandler.setPulseExpandAbortListener(() -> {
786             if (mQs != null) {
787                 mQs.animateHeaderSlidingOut();
788             }
789         });
790         mThemeResId = mView.getContext().getThemeResId();
791         mKeyguardBypassController = bypassController;
792         mUpdateMonitor = keyguardUpdateMonitor;
793         mFirstBypassAttempt = mKeyguardBypassController.getBypassEnabled();
794         KeyguardStateController.Callback
795                 keyguardMonitorCallback =
796                 new KeyguardStateController.Callback() {
797                     @Override
798                     public void onKeyguardFadingAwayChanged() {
799                         if (!mKeyguardStateController.isKeyguardFadingAway()) {
800                             mFirstBypassAttempt = false;
801                             mDelayShowingKeyguardStatusBar = false;
802                         }
803                     }
804                 };
805         mLockscreenShadeTransitionController = lockscreenShadeTransitionController;
806         lockscreenShadeTransitionController.setNotificationPanelController(this);
807         mKeyguardStateController.addCallback(keyguardMonitorCallback);
808         DynamicPrivacyControlListener
809                 dynamicPrivacyControlListener =
810                 new DynamicPrivacyControlListener();
811         dynamicPrivacyController.addListener(dynamicPrivacyControlListener);
812 
813         mBottomAreaShadeAlphaAnimator = ValueAnimator.ofFloat(1f, 0);
814         mBottomAreaShadeAlphaAnimator.addUpdateListener(animation -> {
815             mBottomAreaShadeAlpha = (float) animation.getAnimatedValue();
816             updateKeyguardBottomAreaAlpha();
817         });
818         mBottomAreaShadeAlphaAnimator.setDuration(160);
819         mBottomAreaShadeAlphaAnimator.setInterpolator(Interpolators.ALPHA_OUT);
820         mLockscreenUserManager = notificationLockscreenUserManager;
821         mEntryManager = notificationEntryManager;
822         mConversationNotificationManager = conversationNotificationManager;
823         mAuthController = authController;
824         mLockIconViewController = lockIconViewController;
825         mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController;
826         mRemoteInputManager = remoteInputManager;
827 
828         int currentMode = navigationModeController.addListener(
829                 mode -> mIsGestureNavigation = QuickStepContract.isGesturalMode(mode));
830         mIsGestureNavigation = QuickStepContract.isGesturalMode(currentMode);
831 
832         mView.setBackgroundColor(Color.TRANSPARENT);
833         OnAttachStateChangeListener onAttachStateChangeListener = new OnAttachStateChangeListener();
834         mView.addOnAttachStateChangeListener(onAttachStateChangeListener);
835         if (mView.isAttachedToWindow()) {
836             onAttachStateChangeListener.onViewAttachedToWindow(mView);
837         }
838 
839         mView.setOnApplyWindowInsetsListener(new OnApplyWindowInsetsListener());
840 
841         if (DEBUG) {
842             mView.getOverlay().add(new DebugDrawable());
843         }
844 
845         mMaxKeyguardNotifications = resources.getInteger(R.integer.keyguard_max_notification_count);
846         updateUserSwitcherFlags();
847         onFinishInflate();
848     }
849 
onFinishInflate()850     private void onFinishInflate() {
851         loadDimens();
852         mKeyguardStatusBar = mView.findViewById(R.id.keyguard_header);
853         mBigClockContainer = mView.findViewById(R.id.big_clock_container);
854 
855         UserAvatarView userAvatarView = null;
856         KeyguardUserSwitcherView keyguardUserSwitcherView = null;
857 
858         if (mKeyguardUserSwitcherEnabled && mUserManager.isUserSwitcherEnabled()) {
859             if (mKeyguardQsUserSwitchEnabled) {
860                 ViewStub stub = mView.findViewById(R.id.keyguard_qs_user_switch_stub);
861                 userAvatarView = (UserAvatarView) stub.inflate();
862             } else {
863                 ViewStub stub = mView.findViewById(R.id.keyguard_user_switcher_stub);
864                 keyguardUserSwitcherView = (KeyguardUserSwitcherView) stub.inflate();
865             }
866         }
867 
868         updateViewControllers(
869                 mView.findViewById(R.id.keyguard_status_view),
870                 userAvatarView,
871                 mKeyguardStatusBar,
872                 keyguardUserSwitcherView);
873         mNotificationContainerParent = mView.findViewById(R.id.notification_container_parent);
874         NotificationStackScrollLayout stackScrollLayout = mView.findViewById(
875                 R.id.notification_stack_scroller);
876         mNotificationStackScrollLayoutController.attach(stackScrollLayout);
877         mNotificationStackScrollLayoutController.setOnHeightChangedListener(
878                 mOnHeightChangedListener);
879         mNotificationStackScrollLayoutController.setOverscrollTopChangedListener(
880                 mOnOverscrollTopChangedListener);
881         mNotificationStackScrollLayoutController.setOnScrollListener(this::onNotificationScrolled);
882         mNotificationStackScrollLayoutController.setOnStackYChanged(this::onStackYChanged);
883         mNotificationStackScrollLayoutController.setOnEmptySpaceClickListener(
884                 mOnEmptySpaceClickListener);
885         addTrackingHeadsUpListener(mNotificationStackScrollLayoutController::setTrackingHeadsUp);
886         mKeyguardBottomArea = mView.findViewById(R.id.keyguard_bottom_area);
887         mPreviewContainer = mView.findViewById(R.id.preview_container);
888         mKeyguardBottomArea.setPreviewContainer(mPreviewContainer);
889         mLastOrientation = mResources.getConfiguration().orientation;
890 
891         initBottomArea();
892 
893         mWakeUpCoordinator.setStackScroller(mNotificationStackScrollLayoutController);
894         mQsFrame = mView.findViewById(R.id.qs_frame);
895         mPulseExpansionHandler.setUp(mNotificationStackScrollLayoutController);
896         mWakeUpCoordinator.addListener(new NotificationWakeUpCoordinator.WakeUpListener() {
897             @Override
898             public void onFullyHiddenChanged(boolean isFullyHidden) {
899                 updateKeyguardStatusBarForHeadsUp();
900             }
901 
902             @Override
903             public void onPulseExpansionChanged(boolean expandingChanged) {
904                 if (mKeyguardBypassController.getBypassEnabled()) {
905                     // Position the notifications while dragging down while pulsing
906                     requestScrollerTopPaddingUpdate(false /* animate */);
907                 }
908             }
909         });
910 
911         mView.setRtlChangeListener(layoutDirection -> {
912             if (layoutDirection != mOldLayoutDirection) {
913                 mAffordanceHelper.onRtlPropertiesChanged();
914                 mOldLayoutDirection = layoutDirection;
915             }
916         });
917 
918         mView.setAccessibilityDelegate(mAccessibilityDelegate);
919         if (mShouldUseSplitNotificationShade) {
920             updateResources();
921         }
922 
923         mTapAgainViewController.init();
924     }
925 
926     @Override
loadDimens()927     protected void loadDimens() {
928         super.loadDimens();
929         mFlingAnimationUtils = mFlingAnimationUtilsBuilder.get()
930                 .setMaxLengthSeconds(0.4f).build();
931         mStatusBarMinHeight = mResources.getDimensionPixelSize(
932                 com.android.internal.R.dimen.status_bar_height);
933         mStatusBarHeaderHeightKeyguard = mResources.getDimensionPixelSize(
934                 R.dimen.status_bar_header_height_keyguard);
935         mQsPeekHeight = mResources.getDimensionPixelSize(R.dimen.qs_peek_height);
936         mNotificationsHeaderCollideDistance = mResources.getDimensionPixelSize(
937                 R.dimen.header_notifications_collide_distance);
938         mClockPositionAlgorithm.loadDimens(mResources);
939         mQsFalsingThreshold = mResources.getDimensionPixelSize(R.dimen.qs_falsing_threshold);
940         mPositionMinSideMargin = mResources.getDimensionPixelSize(
941                 R.dimen.notification_panel_min_side_margin);
942         mIndicationBottomPadding = mResources.getDimensionPixelSize(
943                 R.dimen.keyguard_indication_bottom_padding);
944         mShelfHeight = mResources.getDimensionPixelSize(R.dimen.notification_shelf_height);
945         mDarkIconSize = mResources.getDimensionPixelSize(R.dimen.status_bar_icon_drawing_size_dark);
946         int statusbarHeight = mResources.getDimensionPixelSize(
947                 com.android.internal.R.dimen.status_bar_height);
948         mHeadsUpInset = statusbarHeight + mResources.getDimensionPixelSize(
949                 R.dimen.heads_up_status_bar_padding);
950         mDistanceForQSFullShadeTransition = mResources.getDimensionPixelSize(
951                 R.dimen.lockscreen_shade_qs_transition_distance);
952         mMaxOverscrollAmountForPulse = mResources.getDimensionPixelSize(
953                 R.dimen.pulse_expansion_max_top_overshoot);
954         mScrimCornerRadius = mResources.getDimensionPixelSize(
955                 R.dimen.notification_scrim_corner_radius);
956         mScreenCornerRadius = (int) ScreenDecorationsUtils.getWindowCornerRadius(mResources);
957         mLockscreenNotificationQSPadding = mResources.getDimensionPixelSize(
958                 R.dimen.notification_side_paddings);
959     }
960 
updateViewControllers(KeyguardStatusView keyguardStatusView, UserAvatarView userAvatarView, KeyguardStatusBarView keyguardStatusBarView, KeyguardUserSwitcherView keyguardUserSwitcherView)961     private void updateViewControllers(KeyguardStatusView keyguardStatusView,
962             UserAvatarView userAvatarView,
963             KeyguardStatusBarView keyguardStatusBarView,
964             KeyguardUserSwitcherView keyguardUserSwitcherView) {
965         // Re-associate the KeyguardStatusViewController
966         KeyguardStatusViewComponent statusViewComponent =
967                 mKeyguardStatusViewComponentFactory.build(keyguardStatusView);
968         mKeyguardStatusViewController = statusViewComponent.getKeyguardStatusViewController();
969         mKeyguardStatusViewController.init();
970 
971         KeyguardStatusBarViewComponent statusBarViewComponent =
972                 mKeyguardStatusBarViewComponentFactory.build(keyguardStatusBarView);
973         mKeyguarStatusBarViewController =
974                 statusBarViewComponent.getKeyguardStatusBarViewController();
975         mKeyguarStatusBarViewController.init();
976 
977         if (mKeyguardUserSwitcherController != null) {
978             // Try to close the switcher so that callbacks are triggered if necessary.
979             // Otherwise, NPV can get into a state where some of the views are still hidden
980             mKeyguardUserSwitcherController.closeSwitcherIfOpenAndNotSimple(false);
981         }
982 
983         mKeyguardQsUserSwitchController = null;
984         mKeyguardUserSwitcherController = null;
985 
986         // Re-associate the KeyguardUserSwitcherController
987         if (userAvatarView != null) {
988             KeyguardQsUserSwitchComponent userSwitcherComponent =
989                     mKeyguardQsUserSwitchComponentFactory.build(userAvatarView);
990             mKeyguardQsUserSwitchController =
991                     userSwitcherComponent.getKeyguardQsUserSwitchController();
992             mKeyguardQsUserSwitchController.setNotificationPanelViewController(this);
993             mKeyguardQsUserSwitchController.init();
994             mKeyguardStatusBar.setKeyguardUserSwitcherEnabled(true);
995         } else if (keyguardUserSwitcherView != null) {
996             KeyguardUserSwitcherComponent userSwitcherComponent =
997                     mKeyguardUserSwitcherComponentFactory.build(keyguardUserSwitcherView);
998             mKeyguardUserSwitcherController =
999                     userSwitcherComponent.getKeyguardUserSwitcherController();
1000             mKeyguardUserSwitcherController.init();
1001             mKeyguardStatusBar.setKeyguardUserSwitcherEnabled(true);
1002         } else {
1003             mKeyguardStatusBar.setKeyguardUserSwitcherEnabled(false);
1004         }
1005     }
1006 
1007     /**
1008      * Returns if there's a custom clock being presented.
1009      */
hasCustomClock()1010     public boolean hasCustomClock() {
1011         return mKeyguardStatusViewController.hasCustomClock();
1012     }
1013 
setStatusBar(StatusBar bar)1014     private void setStatusBar(StatusBar bar) {
1015         // TODO: this can be injected.
1016         mStatusBar = bar;
1017         mKeyguardBottomArea.setStatusBar(mStatusBar);
1018     }
1019 
updateResources()1020     public void updateResources() {
1021         mQuickQsOffsetHeight = mResources.getDimensionPixelSize(
1022                 com.android.internal.R.dimen.quick_qs_offset_height);
1023         mSplitShadeNotificationsTopPadding =
1024                 mResources.getDimensionPixelSize(R.dimen.notifications_top_padding_split_shade);
1025         int qsWidth = mResources.getDimensionPixelSize(R.dimen.qs_panel_width);
1026         int panelWidth = mResources.getDimensionPixelSize(R.dimen.notification_panel_width);
1027         mShouldUseSplitNotificationShade =
1028                 Utils.shouldUseSplitNotificationShade(mFeatureFlags, mResources);
1029         mScrimController.setClipsQsScrim(!mShouldUseSplitNotificationShade);
1030         if (mQs != null) {
1031             mQs.setTranslateWhileExpanding(mShouldUseSplitNotificationShade);
1032         }
1033         // To change the constraints at runtime, all children of the ConstraintLayout must have ids
1034         ensureAllViewsHaveIds(mNotificationContainerParent);
1035         ConstraintSet constraintSet = new ConstraintSet();
1036         constraintSet.clone(mNotificationContainerParent);
1037         if (mShouldUseSplitNotificationShade) {
1038             // width = 0 to take up all available space within constraints
1039             qsWidth = 0;
1040             panelWidth = 0;
1041             constraintSet.connect(R.id.qs_frame, END, R.id.qs_edge_guideline, END);
1042             constraintSet.connect(
1043                     R.id.notification_stack_scroller, START,
1044                     R.id.qs_edge_guideline, START);
1045             constraintSet.connect(R.id.keyguard_status_view, END, R.id.qs_edge_guideline, END);
1046         } else {
1047             constraintSet.connect(R.id.qs_frame, END, PARENT_ID, END);
1048             constraintSet.connect(R.id.notification_stack_scroller, START, PARENT_ID, START);
1049             constraintSet.connect(R.id.keyguard_status_view, END, PARENT_ID, END);
1050         }
1051         constraintSet.getConstraint(R.id.notification_stack_scroller).layout.mWidth = panelWidth;
1052         constraintSet.getConstraint(R.id.qs_frame).layout.mWidth = qsWidth;
1053         constraintSet.applyTo(mNotificationContainerParent);
1054 
1055         mKeyguardMediaController.refreshMediaPosition();
1056     }
1057 
ensureAllViewsHaveIds(ViewGroup parentView)1058     private static void ensureAllViewsHaveIds(ViewGroup parentView) {
1059         for (int i = 0; i < parentView.getChildCount(); i++) {
1060             View childView = parentView.getChildAt(i);
1061             if (childView.getId() == View.NO_ID) {
1062                 childView.setId(View.generateViewId());
1063             }
1064         }
1065     }
1066 
reInflateStub(int viewId, int stubId, int layoutId, boolean enabled)1067     private View reInflateStub(int viewId, int stubId, int layoutId, boolean enabled) {
1068         View view = mView.findViewById(viewId);
1069         if (view != null) {
1070             int index = mView.indexOfChild(view);
1071             mView.removeView(view);
1072             if (enabled) {
1073                 view = mLayoutInflater.inflate(layoutId, mView, false);
1074                 mView.addView(view, index);
1075             } else {
1076                 // Add the stub back so we can re-inflate it again if necessary
1077                 ViewStub stub = new ViewStub(mView.getContext(), layoutId);
1078                 stub.setId(stubId);
1079                 mView.addView(stub, index);
1080                 view = null;
1081             }
1082         } else if (enabled) {
1083             // It's possible the stub was never inflated if the configuration changed
1084             ViewStub stub = mView.findViewById(stubId);
1085             view = stub.inflate();
1086         }
1087         return view;
1088     }
1089 
reInflateViews()1090     private void reInflateViews() {
1091         if (DEBUG) Log.d(TAG, "reInflateViews");
1092         // Re-inflate the status view group.
1093         KeyguardStatusView keyguardStatusView =
1094                 mNotificationContainerParent.findViewById(R.id.keyguard_status_view);
1095         int statusIndex = mNotificationContainerParent.indexOfChild(keyguardStatusView);
1096         mNotificationContainerParent.removeView(keyguardStatusView);
1097         keyguardStatusView = (KeyguardStatusView) mLayoutInflater.inflate(
1098                 R.layout.keyguard_status_view, mNotificationContainerParent, false);
1099         mNotificationContainerParent.addView(keyguardStatusView, statusIndex);
1100         attachSplitShadeMediaPlayerContainer(
1101                 keyguardStatusView.findViewById(R.id.status_view_media_container));
1102 
1103         // we need to update KeyguardStatusView constraints after reinflating it
1104         updateResources();
1105 
1106         // Re-inflate the keyguard user switcher group.
1107         updateUserSwitcherFlags();
1108         boolean isUserSwitcherEnabled = mUserManager.isUserSwitcherEnabled();
1109         boolean showQsUserSwitch = mKeyguardQsUserSwitchEnabled && isUserSwitcherEnabled;
1110         boolean showKeyguardUserSwitcher =
1111                 !mKeyguardQsUserSwitchEnabled
1112                         && mKeyguardUserSwitcherEnabled
1113                         && isUserSwitcherEnabled;
1114         UserAvatarView userAvatarView = (UserAvatarView) reInflateStub(
1115                 R.id.keyguard_qs_user_switch_view /* viewId */,
1116                 R.id.keyguard_qs_user_switch_stub /* stubId */,
1117                 R.layout.keyguard_qs_user_switch /* layoutId */,
1118                 showQsUserSwitch /* enabled */);
1119         KeyguardUserSwitcherView keyguardUserSwitcherView =
1120                 (KeyguardUserSwitcherView) reInflateStub(
1121                         R.id.keyguard_user_switcher_view /* viewId */,
1122                         R.id.keyguard_user_switcher_stub /* stubId */,
1123                         R.layout.keyguard_user_switcher /* layoutId */,
1124                         showKeyguardUserSwitcher /* enabled */);
1125 
1126         mBigClockContainer.removeAllViews();
1127         updateViewControllers(mView.findViewById(R.id.keyguard_status_view), userAvatarView,
1128                 mKeyguardStatusBar, keyguardUserSwitcherView);
1129 
1130         // Update keyguard bottom area
1131         int index = mView.indexOfChild(mKeyguardBottomArea);
1132         mView.removeView(mKeyguardBottomArea);
1133         KeyguardBottomAreaView oldBottomArea = mKeyguardBottomArea;
1134         mKeyguardBottomArea = (KeyguardBottomAreaView) mLayoutInflater.inflate(
1135                 R.layout.keyguard_bottom_area, mView, false);
1136         mKeyguardBottomArea.initFrom(oldBottomArea);
1137         mKeyguardBottomArea.setPreviewContainer(mPreviewContainer);
1138         mView.addView(mKeyguardBottomArea, index);
1139         initBottomArea();
1140         mKeyguardIndicationController.setIndicationArea(mKeyguardBottomArea);
1141         mStatusBarStateListener.onDozeAmountChanged(mStatusBarStateController.getDozeAmount(),
1142                 mStatusBarStateController.getInterpolatedDozeAmount());
1143 
1144         if (mKeyguardStatusBar != null) {
1145             mKeyguardStatusBar.onThemeChanged();
1146         }
1147 
1148         mKeyguardStatusViewController.setKeyguardStatusViewVisibility(
1149                 mBarState,
1150                 false,
1151                 false,
1152                 mBarState);
1153         if (mKeyguardQsUserSwitchController != null) {
1154             mKeyguardQsUserSwitchController.setKeyguardQsUserSwitchVisibility(
1155                     mBarState,
1156                     false,
1157                     false,
1158                     mBarState);
1159         }
1160         if (mKeyguardUserSwitcherController != null) {
1161             mKeyguardUserSwitcherController.setKeyguardUserSwitcherVisibility(
1162                     mBarState,
1163                     false,
1164                     false,
1165                     mBarState);
1166         }
1167         setKeyguardBottomAreaVisibility(mBarState, false);
1168     }
1169 
attachSplitShadeMediaPlayerContainer(FrameLayout container)1170     private void attachSplitShadeMediaPlayerContainer(FrameLayout container) {
1171         mKeyguardMediaController.attachSplitShadeContainer(container);
1172     }
1173 
initBottomArea()1174     private void initBottomArea() {
1175         mAffordanceHelper = new KeyguardAffordanceHelper(
1176                 mKeyguardAffordanceHelperCallback, mView.getContext(), mFalsingManager);
1177         mKeyguardBottomArea.setAffordanceHelper(mAffordanceHelper);
1178         mKeyguardBottomArea.setStatusBar(mStatusBar);
1179         mKeyguardBottomArea.setUserSetupComplete(mUserSetupComplete);
1180         mKeyguardBottomArea.setFalsingManager(mFalsingManager);
1181         mKeyguardBottomArea.initWallet(mQuickAccessWalletController);
1182         mKeyguardBottomArea.initControls(mControlsComponent);
1183     }
1184 
updateMaxDisplayedNotifications(boolean recompute)1185     private void updateMaxDisplayedNotifications(boolean recompute) {
1186         if (recompute) {
1187             mMaxAllowedKeyguardNotifications = Math.max(computeMaxKeyguardNotifications(), 1);
1188         }
1189 
1190         if (mKeyguardShowing && !mKeyguardBypassController.getBypassEnabled()) {
1191             mNotificationStackScrollLayoutController.setMaxDisplayedNotifications(
1192                     mMaxAllowedKeyguardNotifications);
1193         } else {
1194             // no max when not on the keyguard
1195             mNotificationStackScrollLayoutController.setMaxDisplayedNotifications(-1);
1196         }
1197     }
1198 
setKeyguardIndicationController(KeyguardIndicationController indicationController)1199     public void setKeyguardIndicationController(KeyguardIndicationController indicationController) {
1200         mKeyguardIndicationController = indicationController;
1201         mKeyguardIndicationController.setIndicationArea(mKeyguardBottomArea);
1202     }
1203 
updateGestureExclusionRect()1204     private void updateGestureExclusionRect() {
1205         Rect exclusionRect = calculateGestureExclusionRect();
1206         mView.setSystemGestureExclusionRects(exclusionRect.isEmpty() ? Collections.EMPTY_LIST
1207                 : Collections.singletonList(exclusionRect));
1208     }
1209 
calculateGestureExclusionRect()1210     private Rect calculateGestureExclusionRect() {
1211         Rect exclusionRect = null;
1212         Region touchableRegion = mStatusBarTouchableRegionManager.calculateTouchableRegion();
1213         if (isFullyCollapsed() && touchableRegion != null) {
1214             // Note: The manager also calculates the non-pinned touchable region
1215             exclusionRect = touchableRegion.getBounds();
1216         }
1217         return exclusionRect != null ? exclusionRect : EMPTY_RECT;
1218     }
1219 
setIsFullWidth(boolean isFullWidth)1220     private void setIsFullWidth(boolean isFullWidth) {
1221         mIsFullWidth = isFullWidth;
1222         mNotificationStackScrollLayoutController.setIsFullWidth(isFullWidth);
1223     }
1224 
startQsSizeChangeAnimation(int oldHeight, final int newHeight)1225     private void startQsSizeChangeAnimation(int oldHeight, final int newHeight) {
1226         if (mQsSizeChangeAnimator != null) {
1227             oldHeight = (int) mQsSizeChangeAnimator.getAnimatedValue();
1228             mQsSizeChangeAnimator.cancel();
1229         }
1230         mQsSizeChangeAnimator = ValueAnimator.ofInt(oldHeight, newHeight);
1231         mQsSizeChangeAnimator.setDuration(300);
1232         mQsSizeChangeAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
1233         mQsSizeChangeAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
1234             @Override
1235             public void onAnimationUpdate(ValueAnimator animation) {
1236                 requestScrollerTopPaddingUpdate(false /* animate */);
1237                 requestPanelHeightUpdate();
1238                 int height = (int) mQsSizeChangeAnimator.getAnimatedValue();
1239                 mQs.setHeightOverride(height);
1240             }
1241         });
1242         mQsSizeChangeAnimator.addListener(new AnimatorListenerAdapter() {
1243             @Override
1244             public void onAnimationEnd(Animator animation) {
1245                 mQsSizeChangeAnimator = null;
1246             }
1247         });
1248         mQsSizeChangeAnimator.start();
1249     }
1250 
1251     /**
1252      * Positions the clock and notifications dynamically depending on how many notifications are
1253      * showing.
1254      */
positionClockAndNotifications()1255     private void positionClockAndNotifications() {
1256         positionClockAndNotifications(false /* forceUpdate */);
1257     }
1258 
1259     /**
1260      * Positions the clock and notifications dynamically depending on how many notifications are
1261      * showing.
1262      *
1263      * @param forceClockUpdate Should the clock be updated even when not on keyguard
1264      */
positionClockAndNotifications(boolean forceClockUpdate)1265     private void positionClockAndNotifications(boolean forceClockUpdate) {
1266         boolean animate = mNotificationStackScrollLayoutController.isAddOrRemoveAnimationPending();
1267         int stackScrollerPadding;
1268         boolean onKeyguard = isOnKeyguard();
1269         if (onKeyguard || forceClockUpdate) {
1270             updateClockAppearance();
1271         }
1272         if (!onKeyguard) {
1273             stackScrollerPadding = getUnlockedStackScrollerPadding();
1274         } else {
1275             stackScrollerPadding = mClockPositionResult.stackScrollerPaddingExpanded;
1276         }
1277 
1278         mNotificationStackScrollLayoutController.setIntrinsicPadding(stackScrollerPadding);
1279         mKeyguardBottomArea.setAntiBurnInOffsetX(mClockPositionResult.clockX);
1280 
1281         mStackScrollerMeasuringPass++;
1282         requestScrollerTopPaddingUpdate(animate);
1283         mStackScrollerMeasuringPass = 0;
1284         mAnimateNextPositionUpdate = false;
1285     }
1286 
updateClockAppearance()1287     private void updateClockAppearance() {
1288         int totalHeight = mView.getHeight();
1289         int bottomPadding = Math.max(mIndicationBottomPadding, mAmbientIndicationBottomPadding);
1290         int userSwitcherPreferredY = mStatusBarHeaderHeightKeyguard;
1291         boolean bypassEnabled = mKeyguardBypassController.getBypassEnabled();
1292         final boolean hasVisibleNotifications = mNotificationStackScrollLayoutController
1293                 .getVisibleNotificationCount() != 0 || mMediaDataManager.hasActiveMedia();
1294         mKeyguardStatusViewController.setHasVisibleNotifications(hasVisibleNotifications);
1295         int userIconHeight = mKeyguardQsUserSwitchController != null
1296                 ? mKeyguardQsUserSwitchController.getUserIconHeight() : 0;
1297         float expandedFraction =
1298                 mUnlockedScreenOffAnimationController.isScreenOffAnimationPlaying()
1299                         ? 1.0f : getExpandedFraction();
1300         float darkamount =
1301                 mUnlockedScreenOffAnimationController.isScreenOffAnimationPlaying()
1302                         ? 1.0f : mInterpolatedDarkAmount;
1303         mClockPositionAlgorithm.setup(mStatusBarHeaderHeightKeyguard,
1304                 totalHeight - bottomPadding,
1305                 mNotificationStackScrollLayoutController.getIntrinsicContentHeight(),
1306                 expandedFraction,
1307                 totalHeight,
1308                 mKeyguardStatusViewController.getLockscreenHeight(),
1309                 userIconHeight,
1310                 userSwitcherPreferredY, hasCustomClock(),
1311                 hasVisibleNotifications, darkamount, mOverStretchAmount,
1312                 bypassEnabled, getUnlockedStackScrollerPadding(),
1313                 computeQsExpansionFraction(),
1314                 mDisplayTopInset,
1315                 mShouldUseSplitNotificationShade);
1316         mClockPositionAlgorithm.run(mClockPositionResult);
1317         boolean animate = mNotificationStackScrollLayoutController.isAddOrRemoveAnimationPending();
1318         boolean animateClock = animate || mAnimateNextPositionUpdate;
1319         mKeyguardStatusViewController.updatePosition(
1320                 mClockPositionResult.clockX, mClockPositionResult.clockY,
1321                 mClockPositionResult.clockScale, animateClock);
1322         if (mKeyguardQsUserSwitchController != null) {
1323             mKeyguardQsUserSwitchController.updatePosition(
1324                     mClockPositionResult.clockX,
1325                     mClockPositionResult.userSwitchY,
1326                     animateClock);
1327         }
1328         if (mKeyguardUserSwitcherController != null) {
1329             mKeyguardUserSwitcherController.updatePosition(
1330                     mClockPositionResult.clockX,
1331                     mClockPositionResult.userSwitchY,
1332                     animateClock);
1333         }
1334         updateNotificationTranslucency();
1335         updateClock();
1336     }
1337 
1338     /**
1339      * @return the padding of the stackscroller when unlocked
1340      */
getUnlockedStackScrollerPadding()1341     private int getUnlockedStackScrollerPadding() {
1342         return (mQs != null ? mQs.getHeader().getHeight() : 0) + mQsPeekHeight;
1343     }
1344 
1345     /**
1346      * @return the maximum keyguard notifications that can fit on the screen
1347      */
computeMaxKeyguardNotifications()1348     private int computeMaxKeyguardNotifications() {
1349         float minPadding = mClockPositionAlgorithm.getMinStackScrollerPadding();
1350         int notificationPadding = Math.max(
1351                 1, mResources.getDimensionPixelSize(R.dimen.notification_divider_height));
1352         float shelfSize =
1353                 mNotificationShelfController.getVisibility() == View.GONE
1354                         ? 0
1355                         : mNotificationShelfController.getIntrinsicHeight() + notificationPadding;
1356 
1357         float lockIconPadding = 0;
1358         if (mLockIconViewController.getTop() != 0) {
1359             lockIconPadding = mStatusBar.getDisplayHeight() - mLockIconViewController.getTop()
1360                 + mResources.getDimensionPixelSize(R.dimen.min_lock_icon_padding);
1361         }
1362 
1363         float bottomPadding = Math.max(mIndicationBottomPadding, mAmbientIndicationBottomPadding);
1364         bottomPadding = Math.max(lockIconPadding, bottomPadding);
1365 
1366         float availableSpace =
1367                 mNotificationStackScrollLayoutController.getHeight()
1368                         - minPadding
1369                         - shelfSize
1370                         - bottomPadding;
1371 
1372         int count = 0;
1373         ExpandableView previousView = null;
1374         for (int i = 0; i < mNotificationStackScrollLayoutController.getChildCount(); i++) {
1375             ExpandableView child = mNotificationStackScrollLayoutController.getChildAt(i);
1376             if (child instanceof ExpandableNotificationRow) {
1377                 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
1378                 boolean suppressedSummary = mGroupManager != null
1379                         && mGroupManager.isSummaryOfSuppressedGroup(row.getEntry().getSbn());
1380                 if (suppressedSummary) {
1381                     continue;
1382                 }
1383                 if (!canShowViewOnLockscreen(child)) {
1384                     continue;
1385                 }
1386                 if (row.isRemoved()) {
1387                     continue;
1388                 }
1389             } else if (child instanceof MediaHeaderView) {
1390                 if (child.getVisibility() == GONE) {
1391                     continue;
1392                 }
1393                 if (child.getIntrinsicHeight() == 0) {
1394                     continue;
1395                 }
1396             } else {
1397                 continue;
1398             }
1399             availableSpace -= child.getMinHeight(true /* ignoreTemporaryStates */);
1400             availableSpace -= count == 0 ? 0 : notificationPadding;
1401             availableSpace -= mNotificationStackScrollLayoutController
1402                     .calculateGapHeight(previousView, child, count);
1403             previousView = child;
1404             if (availableSpace >= 0
1405                     && (mMaxKeyguardNotifications == -1 || count < mMaxKeyguardNotifications)) {
1406                 count++;
1407             } else if (availableSpace > -shelfSize) {
1408                 // if we are exactly the last view, then we can show us still!
1409                 int childCount = mNotificationStackScrollLayoutController.getChildCount();
1410                 for (int j = i + 1; j < childCount; j++) {
1411                     ExpandableView view = mNotificationStackScrollLayoutController.getChildAt(j);
1412                     if (view instanceof ExpandableNotificationRow
1413                             && canShowViewOnLockscreen(view)) {
1414                         return count;
1415                     }
1416                 }
1417                 count++;
1418                 return count;
1419             } else {
1420                 return count;
1421             }
1422         }
1423         return count;
1424     }
1425 
1426     /**
1427      * Can a view be shown on the lockscreen when calculating the number of allowed notifications
1428      * to show?
1429      *
1430      * @param child the view in question
1431      * @return true if it can be shown
1432      */
canShowViewOnLockscreen(ExpandableView child)1433     private boolean canShowViewOnLockscreen(ExpandableView child) {
1434         if (child.hasNoContentHeight()) {
1435             return false;
1436         }
1437         if (child instanceof ExpandableNotificationRow &&
1438                 !canShowRowOnLockscreen((ExpandableNotificationRow) child)) {
1439             return false;
1440         } else if (child.getVisibility() == GONE) {
1441             // ENRs can be gone and count because their visibility is only set after
1442             // this calculation, but all other views should be up to date
1443             return false;
1444         }
1445         return true;
1446     }
1447 
1448     /**
1449      * Can a row be shown on the lockscreen when calculating the number of allowed notifications
1450      * to show?
1451      *
1452      * @param row the row in question
1453      * @return true if it can be shown
1454      */
canShowRowOnLockscreen(ExpandableNotificationRow row)1455     private boolean canShowRowOnLockscreen(ExpandableNotificationRow row) {
1456         boolean suppressedSummary =
1457                 mGroupManager != null && mGroupManager.isSummaryOfSuppressedGroup(
1458                         row.getEntry().getSbn());
1459         if (suppressedSummary) {
1460             return false;
1461         }
1462         if (!mLockscreenUserManager.shouldShowOnKeyguard(row.getEntry())) {
1463             return false;
1464         }
1465         if (row.isRemoved()) {
1466             return false;
1467         }
1468         return true;
1469     }
1470 
updateClock()1471     private void updateClock() {
1472         float alpha = mClockPositionResult.clockAlpha * mKeyguardOnlyContentAlpha;
1473         mKeyguardStatusViewController.setAlpha(alpha);
1474         if (mKeyguardQsUserSwitchController != null) {
1475             mKeyguardQsUserSwitchController.setAlpha(alpha);
1476         }
1477         if (mKeyguardUserSwitcherController != null) {
1478             mKeyguardUserSwitcherController.setAlpha(alpha);
1479         }
1480     }
1481 
animateToFullShade(long delay)1482     public void animateToFullShade(long delay) {
1483         mNotificationStackScrollLayoutController.goToFullShade(delay);
1484         mView.requestLayout();
1485         mAnimateNextPositionUpdate = true;
1486     }
1487 
setQsExpansionEnabled()1488     private void setQsExpansionEnabled() {
1489         if (mQs == null) return;
1490         mQs.setHeaderClickable(isQsExpansionEnabled());
1491     }
1492 
setQsExpansionEnabledPolicy(boolean qsExpansionEnabledPolicy)1493     public void setQsExpansionEnabledPolicy(boolean qsExpansionEnabledPolicy) {
1494         mQsExpansionEnabledPolicy = qsExpansionEnabledPolicy;
1495         setQsExpansionEnabled();
1496     }
1497 
1498     @Override
resetViews(boolean animate)1499     public void resetViews(boolean animate) {
1500         mIsLaunchTransitionFinished = false;
1501         mBlockTouches = false;
1502         if (!mLaunchingAffordance) {
1503             mAffordanceHelper.reset(false);
1504             mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_AFFORDANCE;
1505         }
1506         mStatusBar.getGutsManager().closeAndSaveGuts(true /* leavebehind */, true /* force */,
1507                 true /* controls */, -1 /* x */, -1 /* y */, true /* resetMenu */);
1508         if (animate && !isFullyCollapsed()) {
1509             animateCloseQs(true /* animateAway */);
1510         } else {
1511             closeQs();
1512         }
1513         mNotificationStackScrollLayoutController.setOverScrollAmount(0f, true /* onTop */, animate,
1514                 !animate /* cancelAnimators */);
1515         mNotificationStackScrollLayoutController.resetScrollPosition();
1516     }
1517 
1518     @Override
collapse(boolean delayed, float speedUpFactor)1519     public void collapse(boolean delayed, float speedUpFactor) {
1520         if (!canPanelBeCollapsed()) {
1521             return;
1522         }
1523 
1524         if (mQsExpanded) {
1525             mQsExpandImmediate = true;
1526             mNotificationStackScrollLayoutController.setShouldShowShelfOnly(true);
1527         }
1528         super.collapse(delayed, speedUpFactor);
1529     }
1530 
closeQs()1531     public void closeQs() {
1532         cancelQsAnimation();
1533         setQsExpansion(mQsMinExpansionHeight);
1534     }
1535 
cancelAnimation()1536     public void cancelAnimation() {
1537         mView.animate().cancel();
1538     }
1539 
1540 
1541     /**
1542      * Animate QS closing by flinging it.
1543      * If QS is expanded, it will collapse into QQS and stop.
1544      *
1545      * @param animateAway Do not stop when QS becomes QQS. Fling until QS isn't visible anymore.
1546      */
animateCloseQs(boolean animateAway)1547     public void animateCloseQs(boolean animateAway) {
1548         if (mQsExpansionAnimator != null) {
1549             if (!mQsAnimatorExpand) {
1550                 return;
1551             }
1552             float height = mQsExpansionHeight;
1553             mQsExpansionAnimator.cancel();
1554             setQsExpansion(height);
1555         }
1556         flingSettings(0 /* vel */, animateAway ? FLING_HIDE : FLING_COLLAPSE);
1557     }
1558 
isQsExpansionEnabled()1559     private boolean isQsExpansionEnabled() {
1560         return mQsExpansionEnabledPolicy && mQsExpansionEnabledAmbient
1561                 && !mRemoteInputManager.getController().isRemoteInputActive();
1562     }
1563 
expandWithQs()1564     public void expandWithQs() {
1565         if (isQsExpansionEnabled()) {
1566             mQsExpandImmediate = true;
1567             mNotificationStackScrollLayoutController.setShouldShowShelfOnly(true);
1568         }
1569         if (isFullyCollapsed()) {
1570             expand(true /* animate */);
1571         } else {
1572             traceQsJank(true /* startTracing */, false /* wasCancelled */);
1573             flingSettings(0 /* velocity */, FLING_EXPAND);
1574         }
1575     }
1576 
expandWithQsDetail(DetailAdapter qsDetailAdapter)1577     public void expandWithQsDetail(DetailAdapter qsDetailAdapter) {
1578         traceQsJank(true /* startTracing */, false /* wasCancelled */);
1579         flingSettings(0 /* velocity */, FLING_EXPAND);
1580         // When expanding with a panel, there's no meaningful touch point to correspond to. Set the
1581         // origin to somewhere above the screen. This is used for animations.
1582         int x = mQsFrame.getWidth() / 2;
1583         mQSDetailDisplayer.showDetailAdapter(qsDetailAdapter, x, -getHeight());
1584         if (mAccessibilityManager.isEnabled()) {
1585             mView.setAccessibilityPaneTitle(determineAccessibilityPaneTitle());
1586         }
1587     }
1588 
expandWithoutQs()1589     public void expandWithoutQs() {
1590         if (isQsExpanded()) {
1591             flingSettings(0 /* velocity */, FLING_COLLAPSE);
1592         } else {
1593             expand(true /* animate */);
1594         }
1595     }
1596 
1597     @Override
fling(float vel, boolean expand)1598     public void fling(float vel, boolean expand) {
1599         GestureRecorder gr = ((PhoneStatusBarView) mBar).mBar.getGestureRecorder();
1600         if (gr != null) {
1601             gr.tag("fling " + ((vel > 0) ? "open" : "closed"), "notifications,v=" + vel);
1602         }
1603         super.fling(vel, expand);
1604     }
1605 
1606     @Override
flingToHeight(float vel, boolean expand, float target, float collapseSpeedUpFactor, boolean expandBecauseOfFalsing)1607     protected void flingToHeight(float vel, boolean expand, float target,
1608             float collapseSpeedUpFactor, boolean expandBecauseOfFalsing) {
1609         mHeadsUpTouchHelper.notifyFling(!expand);
1610         mKeyguardStateController.notifyPanelFlingStart(!expand /* flingingToDismiss */);
1611         setClosingWithAlphaFadeout(!expand && !isOnKeyguard() && getFadeoutAlpha() == 1.0f);
1612         super.flingToHeight(vel, expand, target, collapseSpeedUpFactor, expandBecauseOfFalsing);
1613     }
1614 
onQsIntercept(MotionEvent event)1615     private boolean onQsIntercept(MotionEvent event) {
1616         int pointerIndex = event.findPointerIndex(mTrackingPointer);
1617         if (pointerIndex < 0) {
1618             pointerIndex = 0;
1619             mTrackingPointer = event.getPointerId(pointerIndex);
1620         }
1621         final float x = event.getX(pointerIndex);
1622         final float y = event.getY(pointerIndex);
1623 
1624         switch (event.getActionMasked()) {
1625             case MotionEvent.ACTION_DOWN:
1626                 mInitialTouchY = y;
1627                 mInitialTouchX = x;
1628                 initVelocityTracker();
1629                 trackMovement(event);
1630                 if (mKeyguardShowing
1631                         && shouldQuickSettingsIntercept(mInitialTouchX, mInitialTouchY, 0)) {
1632                     // Dragging down on the lockscreen statusbar should prohibit other interactions
1633                     // immediately, otherwise we'll wait on the touchslop. This is to allow
1634                     // dragging down to expanded quick settings directly on the lockscreen.
1635                     mView.getParent().requestDisallowInterceptTouchEvent(true);
1636                 }
1637                 if (mQsExpansionAnimator != null) {
1638                     mInitialHeightOnTouch = mQsExpansionHeight;
1639                     mQsTracking = true;
1640                     traceQsJank(true /* startTracing */, false /* wasCancelled */);
1641                     mNotificationStackScrollLayoutController.cancelLongPress();
1642                 }
1643                 break;
1644             case MotionEvent.ACTION_POINTER_UP:
1645                 final int upPointer = event.getPointerId(event.getActionIndex());
1646                 if (mTrackingPointer == upPointer) {
1647                     // gesture is ongoing, find a new pointer to track
1648                     final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1;
1649                     mTrackingPointer = event.getPointerId(newIndex);
1650                     mInitialTouchX = event.getX(newIndex);
1651                     mInitialTouchY = event.getY(newIndex);
1652                 }
1653                 break;
1654 
1655             case MotionEvent.ACTION_MOVE:
1656                 final float h = y - mInitialTouchY;
1657                 trackMovement(event);
1658                 if (mQsTracking) {
1659 
1660                     // Already tracking because onOverscrolled was called. We need to update here
1661                     // so we don't stop for a frame until the next touch event gets handled in
1662                     // onTouchEvent.
1663                     setQsExpansion(h + mInitialHeightOnTouch);
1664                     trackMovement(event);
1665                     return true;
1666                 }
1667                 if ((h > getTouchSlop(event) || (h < -getTouchSlop(event) && mQsExpanded))
1668                         && Math.abs(h) > Math.abs(x - mInitialTouchX)
1669                         && shouldQuickSettingsIntercept(mInitialTouchX, mInitialTouchY, h)) {
1670                     mView.getParent().requestDisallowInterceptTouchEvent(true);
1671                     mQsTracking = true;
1672                     traceQsJank(true /* startTracing */, false /* wasCancelled */);
1673                     onQsExpansionStarted();
1674                     notifyExpandingFinished();
1675                     mInitialHeightOnTouch = mQsExpansionHeight;
1676                     mInitialTouchY = y;
1677                     mInitialTouchX = x;
1678                     mNotificationStackScrollLayoutController.cancelLongPress();
1679                     return true;
1680                 }
1681                 break;
1682 
1683             case MotionEvent.ACTION_CANCEL:
1684             case MotionEvent.ACTION_UP:
1685                 trackMovement(event);
1686                 mQsTracking = false;
1687                 break;
1688         }
1689         return false;
1690     }
1691 
1692     @Override
isInContentBounds(float x, float y)1693     protected boolean isInContentBounds(float x, float y) {
1694         float stackScrollerX = mNotificationStackScrollLayoutController.getX();
1695         return !mNotificationStackScrollLayoutController
1696                 .isBelowLastNotification(x - stackScrollerX, y)
1697                 && stackScrollerX < x
1698                 && x < stackScrollerX + mNotificationStackScrollLayoutController.getWidth();
1699     }
1700 
traceQsJank(boolean startTracing, boolean wasCancelled)1701     private void traceQsJank(boolean startTracing, boolean wasCancelled) {
1702         InteractionJankMonitor monitor = InteractionJankMonitor.getInstance();
1703         if (startTracing) {
1704             monitor.begin(mView, CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE);
1705         } else {
1706             if (wasCancelled) {
1707                 monitor.cancel(CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE);
1708             } else {
1709                 monitor.end(CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE);
1710             }
1711         }
1712     }
1713 
initDownStates(MotionEvent event)1714     private void initDownStates(MotionEvent event) {
1715         if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
1716             mOnlyAffordanceInThisMotion = false;
1717             mQsTouchAboveFalsingThreshold = mQsFullyExpanded;
1718             mDozingOnDown = isDozing();
1719             mDownX = event.getX();
1720             mDownY = event.getY();
1721             mCollapsedOnDown = isFullyCollapsed();
1722             mIsPanelCollapseOnQQS = canPanelCollapseOnQQS(mDownX, mDownY);
1723             mListenForHeadsUp = mCollapsedOnDown && mHeadsUpManager.hasPinnedHeadsUp();
1724             mAllowExpandForSmallExpansion = mExpectingSynthesizedDown;
1725             mTouchSlopExceededBeforeDown = mExpectingSynthesizedDown;
1726             if (mExpectingSynthesizedDown) {
1727                 mLastEventSynthesizedDown = true;
1728             } else {
1729                 // down but not synthesized motion event.
1730                 mLastEventSynthesizedDown = false;
1731             }
1732         } else {
1733             // not down event at all.
1734             mLastEventSynthesizedDown = false;
1735         }
1736     }
1737 
1738     /**
1739      * Can the panel collapse in this motion because it was started on QQS?
1740      *
1741      * @param downX the x location where the touch started
1742      * @param downY the y location where the touch started
1743      *
1744      * @return true if the panel could be collapsed because it stared on QQS
1745      */
canPanelCollapseOnQQS(float downX, float downY)1746     private boolean canPanelCollapseOnQQS(float downX, float downY) {
1747         if (mCollapsedOnDown || mKeyguardShowing || mQsExpanded) {
1748             return false;
1749         }
1750         View header = mQs == null ? mKeyguardStatusBar : mQs.getHeader();
1751         return downX >= mQsFrame.getX() && downX <= mQsFrame.getX() + mQsFrame.getWidth()
1752                         && downY <= header.getBottom();
1753 
1754     }
1755 
flingQsWithCurrentVelocity(float y, boolean isCancelMotionEvent)1756     private void flingQsWithCurrentVelocity(float y, boolean isCancelMotionEvent) {
1757         float vel = getCurrentQSVelocity();
1758         boolean expandsQs = flingExpandsQs(vel);
1759         if (expandsQs) {
1760             if (mFalsingManager.isUnlockingDisabled() || isFalseTouch(QUICK_SETTINGS)) {
1761                 expandsQs = false;
1762             } else {
1763                 logQsSwipeDown(y);
1764             }
1765         } else if (vel < 0) {
1766             mFalsingManager.isFalseTouch(QS_COLLAPSE);
1767         }
1768 
1769         flingSettings(vel, expandsQs && !isCancelMotionEvent ? FLING_EXPAND : FLING_COLLAPSE);
1770     }
1771 
logQsSwipeDown(float y)1772     private void logQsSwipeDown(float y) {
1773         float vel = getCurrentQSVelocity();
1774         final int
1775                 gesture =
1776                 mBarState == KEYGUARD ? MetricsEvent.ACTION_LS_QS
1777                         : MetricsEvent.ACTION_SHADE_QS_PULL;
1778         mLockscreenGestureLogger.write(gesture,
1779                 (int) ((y - mInitialTouchY) / mStatusBar.getDisplayDensity()),
1780                 (int) (vel / mStatusBar.getDisplayDensity()));
1781     }
1782 
flingExpandsQs(float vel)1783     private boolean flingExpandsQs(float vel) {
1784         if (Math.abs(vel) < mFlingAnimationUtils.getMinVelocityPxPerSecond()) {
1785             return computeQsExpansionFraction() > 0.5f;
1786         } else {
1787             return vel > 0;
1788         }
1789     }
1790 
isFalseTouch(@lassifier.InteractionType int interactionType)1791     private boolean isFalseTouch(@Classifier.InteractionType int interactionType) {
1792         if (mFalsingManager.isClassifierEnabled()) {
1793             return mFalsingManager.isFalseTouch(interactionType);
1794         }
1795         return !mQsTouchAboveFalsingThreshold;
1796     }
1797 
computeQsExpansionFraction()1798     private float computeQsExpansionFraction() {
1799         if (mQSAnimatingHiddenFromCollapsed) {
1800             // When hiding QS from collapsed state, the expansion can sometimes temporarily
1801             // be larger than 0 because of the timing, leading to flickers.
1802             return 0.0f;
1803         }
1804         return Math.min(
1805                 1f, (mQsExpansionHeight - mQsMinExpansionHeight) / (mQsMaxExpansionHeight
1806                         - mQsMinExpansionHeight));
1807     }
1808 
1809     @Override
shouldExpandWhenNotFlinging()1810     protected boolean shouldExpandWhenNotFlinging() {
1811         if (super.shouldExpandWhenNotFlinging()) {
1812             return true;
1813         }
1814         if (mAllowExpandForSmallExpansion) {
1815             // When we get a touch that came over from launcher, the velocity isn't always correct
1816             // Let's err on expanding if the gesture has been reasonably slow
1817             long timeSinceDown = SystemClock.uptimeMillis() - mDownTime;
1818             return timeSinceDown <= MAX_TIME_TO_OPEN_WHEN_FLINGING_FROM_LAUNCHER;
1819         }
1820         return false;
1821     }
1822 
1823     @Override
getOpeningHeight()1824     protected float getOpeningHeight() {
1825         return mNotificationStackScrollLayoutController.getOpeningHeight();
1826     }
1827 
1828 
handleQsTouch(MotionEvent event)1829     private boolean handleQsTouch(MotionEvent event) {
1830         final int action = event.getActionMasked();
1831         if (action == MotionEvent.ACTION_DOWN && getExpandedFraction() == 1f
1832                 && mBarState != KEYGUARD && !mQsExpanded && isQsExpansionEnabled()) {
1833             // Down in the empty area while fully expanded - go to QS.
1834             mQsTracking = true;
1835             traceQsJank(true /* startTracing */, false /* wasCancelled */);
1836             mConflictingQsExpansionGesture = true;
1837             onQsExpansionStarted();
1838             mInitialHeightOnTouch = mQsExpansionHeight;
1839             mInitialTouchY = event.getX();
1840             mInitialTouchX = event.getY();
1841         }
1842         if (!isFullyCollapsed()) {
1843             handleQsDown(event);
1844         }
1845         if (!mQsExpandImmediate && mQsTracking) {
1846             onQsTouch(event);
1847             if (!mConflictingQsExpansionGesture) {
1848                 return true;
1849             }
1850         }
1851         if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
1852             mConflictingQsExpansionGesture = false;
1853         }
1854         if (action == MotionEvent.ACTION_DOWN && isFullyCollapsed() && isQsExpansionEnabled()) {
1855             mTwoFingerQsExpandPossible = true;
1856         }
1857         if (mTwoFingerQsExpandPossible && isOpenQsEvent(event) && event.getY(event.getActionIndex())
1858                 < mStatusBarMinHeight) {
1859             mMetricsLogger.count(COUNTER_PANEL_OPEN_QS, 1);
1860             mQsExpandImmediate = true;
1861             mNotificationStackScrollLayoutController.setShouldShowShelfOnly(true);
1862             requestPanelHeightUpdate();
1863 
1864             // Normally, we start listening when the panel is expanded, but here we need to start
1865             // earlier so the state is already up to date when dragging down.
1866             setListening(true);
1867         }
1868         return false;
1869     }
1870 
isInQsArea(float x, float y)1871     private boolean isInQsArea(float x, float y) {
1872         if (x < mQsFrame.getX() || x > mQsFrame.getX() + mQsFrame.getWidth()) {
1873             return false;
1874         }
1875         // Let's reject anything at the very bottom around the home handle in gesture nav
1876         if (mIsGestureNavigation && y > mView.getHeight() - mNavigationBarBottomHeight) {
1877             return false;
1878         }
1879         return y <= mNotificationStackScrollLayoutController.getBottomMostNotificationBottom()
1880                         || y <= mQs.getView().getY() + mQs.getView().getHeight();
1881     }
1882 
isOpenQsEvent(MotionEvent event)1883     private boolean isOpenQsEvent(MotionEvent event) {
1884         final int pointerCount = event.getPointerCount();
1885         final int action = event.getActionMasked();
1886 
1887         final boolean
1888                 twoFingerDrag =
1889                 action == MotionEvent.ACTION_POINTER_DOWN && pointerCount == 2;
1890 
1891         final boolean
1892                 stylusButtonClickDrag =
1893                 action == MotionEvent.ACTION_DOWN && (event.isButtonPressed(
1894                         MotionEvent.BUTTON_STYLUS_PRIMARY) || event.isButtonPressed(
1895                         MotionEvent.BUTTON_STYLUS_SECONDARY));
1896 
1897         final boolean
1898                 mouseButtonClickDrag =
1899                 action == MotionEvent.ACTION_DOWN && (event.isButtonPressed(
1900                         MotionEvent.BUTTON_SECONDARY) || event.isButtonPressed(
1901                         MotionEvent.BUTTON_TERTIARY));
1902 
1903         return twoFingerDrag || stylusButtonClickDrag || mouseButtonClickDrag;
1904     }
1905 
handleQsDown(MotionEvent event)1906     private void handleQsDown(MotionEvent event) {
1907         if (event.getActionMasked() == MotionEvent.ACTION_DOWN && shouldQuickSettingsIntercept(
1908                 event.getX(), event.getY(), -1)) {
1909             mFalsingCollector.onQsDown();
1910             mQsTracking = true;
1911             onQsExpansionStarted();
1912             mInitialHeightOnTouch = mQsExpansionHeight;
1913             mInitialTouchY = event.getX();
1914             mInitialTouchX = event.getY();
1915 
1916             // If we interrupt an expansion gesture here, make sure to update the state correctly.
1917             notifyExpandingFinished();
1918         }
1919     }
1920 
1921     /**
1922      * Input focus transfer is about to happen.
1923      */
startWaitingForOpenPanelGesture()1924     public void startWaitingForOpenPanelGesture() {
1925         if (!isFullyCollapsed()) {
1926             return;
1927         }
1928         mExpectingSynthesizedDown = true;
1929         onTrackingStarted();
1930         updatePanelExpanded();
1931     }
1932 
1933     /**
1934      * Called when this view is no longer waiting for input focus transfer.
1935      *
1936      * There are two scenarios behind this function call. First, input focus transfer
1937      * has successfully happened and this view already received synthetic DOWN event.
1938      * (mExpectingSynthesizedDown == false). Do nothing.
1939      *
1940      * Second, before input focus transfer finished, user may have lifted finger
1941      * in previous window and this window never received synthetic DOWN event.
1942      * (mExpectingSynthesizedDown == true).
1943      * In this case, we use the velocity to trigger fling event.
1944      *
1945      * @param velocity unit is in px / millis
1946      */
stopWaitingForOpenPanelGesture(boolean cancel, final float velocity)1947     public void stopWaitingForOpenPanelGesture(boolean cancel, final float velocity) {
1948         if (mExpectingSynthesizedDown) {
1949             mExpectingSynthesizedDown = false;
1950             if (cancel) {
1951                 collapse(false /* delayed */, 1.0f /* speedUpFactor */);
1952             } else {
1953                 maybeVibrateOnOpening();
1954                 fling(velocity > 1f ? 1000f * velocity : 0, true /* expand */);
1955             }
1956             onTrackingStopped(false);
1957         }
1958     }
1959 
1960     @Override
flingExpands(float vel, float vectorVel, float x, float y)1961     protected boolean flingExpands(float vel, float vectorVel, float x, float y) {
1962         boolean expands = super.flingExpands(vel, vectorVel, x, y);
1963 
1964         // If we are already running a QS expansion, make sure that we keep the panel open.
1965         if (mQsExpansionAnimator != null) {
1966             expands = true;
1967         }
1968         return expands;
1969     }
1970 
1971     @Override
shouldGestureWaitForTouchSlop()1972     protected boolean shouldGestureWaitForTouchSlop() {
1973         if (mExpectingSynthesizedDown) {
1974             mExpectingSynthesizedDown = false;
1975             return false;
1976         }
1977         return isFullyCollapsed() || mBarState != StatusBarState.SHADE;
1978     }
1979 
1980     @Override
shouldGestureIgnoreXTouchSlop(float x, float y)1981     protected boolean shouldGestureIgnoreXTouchSlop(float x, float y) {
1982         return !mAffordanceHelper.isOnAffordanceIcon(x, y);
1983     }
1984 
onQsTouch(MotionEvent event)1985     private void onQsTouch(MotionEvent event) {
1986         int pointerIndex = event.findPointerIndex(mTrackingPointer);
1987         if (pointerIndex < 0) {
1988             pointerIndex = 0;
1989             mTrackingPointer = event.getPointerId(pointerIndex);
1990         }
1991         final float y = event.getY(pointerIndex);
1992         final float x = event.getX(pointerIndex);
1993         final float h = y - mInitialTouchY;
1994 
1995         switch (event.getActionMasked()) {
1996             case MotionEvent.ACTION_DOWN:
1997                 mQsTracking = true;
1998                 traceQsJank(true /* startTracing */, false /* wasCancelled */);
1999                 mInitialTouchY = y;
2000                 mInitialTouchX = x;
2001                 onQsExpansionStarted();
2002                 mInitialHeightOnTouch = mQsExpansionHeight;
2003                 initVelocityTracker();
2004                 trackMovement(event);
2005                 break;
2006 
2007             case MotionEvent.ACTION_POINTER_UP:
2008                 final int upPointer = event.getPointerId(event.getActionIndex());
2009                 if (mTrackingPointer == upPointer) {
2010                     // gesture is ongoing, find a new pointer to track
2011                     final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1;
2012                     final float newY = event.getY(newIndex);
2013                     final float newX = event.getX(newIndex);
2014                     mTrackingPointer = event.getPointerId(newIndex);
2015                     mInitialHeightOnTouch = mQsExpansionHeight;
2016                     mInitialTouchY = newY;
2017                     mInitialTouchX = newX;
2018                 }
2019                 break;
2020 
2021             case MotionEvent.ACTION_MOVE:
2022                 setQsExpansion(h + mInitialHeightOnTouch);
2023                 if (h >= getFalsingThreshold()) {
2024                     mQsTouchAboveFalsingThreshold = true;
2025                 }
2026                 trackMovement(event);
2027                 break;
2028 
2029             case MotionEvent.ACTION_UP:
2030             case MotionEvent.ACTION_CANCEL:
2031                 mQsTracking = false;
2032                 mTrackingPointer = -1;
2033                 trackMovement(event);
2034                 float fraction = computeQsExpansionFraction();
2035                 if (fraction != 0f || y >= mInitialTouchY) {
2036                     flingQsWithCurrentVelocity(y,
2037                             event.getActionMasked() == MotionEvent.ACTION_CANCEL);
2038                 } else {
2039                     traceQsJank(false /* startTracing */,
2040                             event.getActionMasked() == MotionEvent.ACTION_CANCEL);
2041                 }
2042                 if (mQsVelocityTracker != null) {
2043                     mQsVelocityTracker.recycle();
2044                     mQsVelocityTracker = null;
2045                 }
2046                 break;
2047         }
2048     }
2049 
getFalsingThreshold()2050     private int getFalsingThreshold() {
2051         float factor = mStatusBar.isWakeUpComingFromTouch() ? 1.5f : 1.0f;
2052         return (int) (mQsFalsingThreshold * factor);
2053     }
2054 
setOverScrolling(boolean overscrolling)2055     private void setOverScrolling(boolean overscrolling) {
2056         mStackScrollerOverscrolling = overscrolling;
2057         if (mQs == null) return;
2058         mQs.setOverscrolling(overscrolling);
2059     }
2060 
onQsExpansionStarted()2061     private void onQsExpansionStarted() {
2062         onQsExpansionStarted(0);
2063     }
2064 
onQsExpansionStarted(int overscrollAmount)2065     protected void onQsExpansionStarted(int overscrollAmount) {
2066         cancelQsAnimation();
2067         cancelHeightAnimator();
2068 
2069         // Reset scroll position and apply that position to the expanded height.
2070         float height = mQsExpansionHeight - overscrollAmount;
2071         setQsExpansion(height);
2072         requestPanelHeightUpdate();
2073         mNotificationStackScrollLayoutController.checkSnoozeLeavebehind();
2074 
2075         // When expanding QS, let's authenticate the user if possible,
2076         // this will speed up notification actions.
2077         if (height == 0) {
2078             mStatusBar.requestFaceAuth(false);
2079         }
2080     }
2081 
setQsExpanded(boolean expanded)2082     @VisibleForTesting void setQsExpanded(boolean expanded) {
2083         boolean changed = mQsExpanded != expanded;
2084         if (changed) {
2085             mQsExpanded = expanded;
2086             updateQsState();
2087             requestPanelHeightUpdate();
2088             mFalsingCollector.setQsExpanded(expanded);
2089             mStatusBar.setQsExpanded(expanded);
2090             mNotificationContainerParent.setQsExpanded(expanded);
2091             mPulseExpansionHandler.setQsExpanded(expanded);
2092             mKeyguardBypassController.setQSExpanded(expanded);
2093             mStatusBarKeyguardViewManager.setQsExpanded(expanded);
2094             mLockIconViewController.setQsExpanded(expanded);
2095             mPrivacyDotViewController.setQsExpanded(expanded);
2096         }
2097     }
2098 
maybeAnimateBottomAreaAlpha()2099     private void maybeAnimateBottomAreaAlpha() {
2100         mBottomAreaShadeAlphaAnimator.cancel();
2101         if (mBarState == StatusBarState.SHADE_LOCKED) {
2102             mBottomAreaShadeAlphaAnimator.setFloatValues(mBottomAreaShadeAlpha, 0.0f);
2103             mBottomAreaShadeAlphaAnimator.start();
2104         } else {
2105             mBottomAreaShadeAlpha = 1f;
2106         }
2107     }
2108 
2109     private final Runnable mAnimateKeyguardStatusBarInvisibleEndRunnable = new Runnable() {
2110         @Override
2111         public void run() {
2112             mKeyguardStatusBar.setVisibility(View.INVISIBLE);
2113             mKeyguardStatusBar.setAlpha(1f);
2114             mKeyguardStatusBarAnimateAlpha = 1f;
2115         }
2116     };
2117 
animateKeyguardStatusBarOut()2118     private void animateKeyguardStatusBarOut() {
2119         ValueAnimator anim = ValueAnimator.ofFloat(mKeyguardStatusBar.getAlpha(), 0f);
2120         anim.addUpdateListener(mStatusBarAnimateAlphaListener);
2121         anim.setStartDelay(mKeyguardStateController.isKeyguardFadingAway()
2122                 ? mKeyguardStateController.getKeyguardFadingAwayDelay() : 0);
2123 
2124         long duration;
2125         if (mKeyguardStateController.isKeyguardFadingAway()) {
2126             duration = mKeyguardStateController.getShortenedFadingAwayDuration();
2127         } else {
2128             duration = StackStateAnimator.ANIMATION_DURATION_STANDARD;
2129         }
2130         anim.setDuration(duration);
2131 
2132         anim.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
2133         anim.addListener(new AnimatorListenerAdapter() {
2134             @Override
2135             public void onAnimationEnd(Animator animation) {
2136                 mAnimateKeyguardStatusBarInvisibleEndRunnable.run();
2137             }
2138         });
2139         anim.start();
2140     }
2141 
2142     private final ValueAnimator.AnimatorUpdateListener
2143             mStatusBarAnimateAlphaListener =
2144             new ValueAnimator.AnimatorUpdateListener() {
2145                 @Override
2146                 public void onAnimationUpdate(ValueAnimator animation) {
2147                     mKeyguardStatusBarAnimateAlpha = (float) animation.getAnimatedValue();
2148                     updateHeaderKeyguardAlpha();
2149                 }
2150             };
2151 
animateKeyguardStatusBarIn(long duration)2152     private void animateKeyguardStatusBarIn(long duration) {
2153         mKeyguardStatusBar.setVisibility(View.VISIBLE);
2154         mKeyguardStatusBar.setAlpha(0f);
2155         ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
2156         anim.addUpdateListener(mStatusBarAnimateAlphaListener);
2157         anim.setDuration(duration);
2158         anim.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
2159         anim.start();
2160     }
2161 
2162     private final Runnable mAnimateKeyguardBottomAreaInvisibleEndRunnable = new Runnable() {
2163         @Override
2164         public void run() {
2165             mKeyguardBottomArea.setVisibility(View.GONE);
2166         }
2167     };
2168 
setKeyguardBottomAreaVisibility(int statusBarState, boolean goingToFullShade)2169     private void setKeyguardBottomAreaVisibility(int statusBarState, boolean goingToFullShade) {
2170         mKeyguardBottomArea.animate().cancel();
2171         if (goingToFullShade) {
2172             mKeyguardBottomArea.animate().alpha(0f).setStartDelay(
2173                     mKeyguardStateController.getKeyguardFadingAwayDelay()).setDuration(
2174                     mKeyguardStateController.getShortenedFadingAwayDuration()).setInterpolator(
2175                     Interpolators.ALPHA_OUT).withEndAction(
2176                     mAnimateKeyguardBottomAreaInvisibleEndRunnable).start();
2177         } else if (statusBarState == KEYGUARD
2178                 || statusBarState == StatusBarState.SHADE_LOCKED) {
2179             mKeyguardBottomArea.setVisibility(View.VISIBLE);
2180             mKeyguardBottomArea.setAlpha(1f);
2181         } else {
2182             mKeyguardBottomArea.setVisibility(View.GONE);
2183         }
2184     }
2185 
updateQsState()2186     private void updateQsState() {
2187         mNotificationStackScrollLayoutController.setQsExpanded(mQsExpanded);
2188         mNotificationStackScrollLayoutController.setScrollingEnabled(
2189                 mBarState != KEYGUARD
2190                         && (!mQsExpanded
2191                             || mQsExpansionFromOverscroll
2192                             || mShouldUseSplitNotificationShade));
2193 
2194         if (mKeyguardUserSwitcherController != null && mQsExpanded
2195                 && !mStackScrollerOverscrolling) {
2196             mKeyguardUserSwitcherController.closeSwitcherIfOpenAndNotSimple(true);
2197         }
2198         if (mQs == null) return;
2199         mQs.setExpanded(mQsExpanded);
2200     }
2201 
setQsExpansion(float height)2202     private void setQsExpansion(float height) {
2203         height = Math.min(Math.max(height, mQsMinExpansionHeight), mQsMaxExpansionHeight);
2204         mQsFullyExpanded = height == mQsMaxExpansionHeight && mQsMaxExpansionHeight != 0;
2205         if (height > mQsMinExpansionHeight && !mQsExpanded && !mStackScrollerOverscrolling
2206                 && !mDozing) {
2207             setQsExpanded(true);
2208         } else if (height <= mQsMinExpansionHeight && mQsExpanded) {
2209             setQsExpanded(false);
2210         }
2211         mQsExpansionHeight = height;
2212         updateQsExpansion();
2213         requestScrollerTopPaddingUpdate(false /* animate */);
2214         updateHeaderKeyguardAlpha();
2215         if (mBarState == StatusBarState.SHADE_LOCKED || mBarState == KEYGUARD) {
2216             updateKeyguardBottomAreaAlpha();
2217             positionClockAndNotifications();
2218             updateBigClockAlpha();
2219         }
2220 
2221         if (mAccessibilityManager.isEnabled()) {
2222             mView.setAccessibilityPaneTitle(determineAccessibilityPaneTitle());
2223         }
2224 
2225         if (!mFalsingManager.isUnlockingDisabled() && mQsFullyExpanded
2226                 && mFalsingCollector.shouldEnforceBouncer()) {
2227             mStatusBar.executeRunnableDismissingKeyguard(null, null /* cancelAction */,
2228                     false /* dismissShade */, true /* afterKeyguardGone */, false /* deferred */);
2229         }
2230         for (int i = 0; i < mExpansionListeners.size(); i++) {
2231             mExpansionListeners.get(i).onQsExpansionChanged(
2232                     mQsMaxExpansionHeight != 0 ? mQsExpansionHeight / mQsMaxExpansionHeight : 0);
2233         }
2234         if (DEBUG) {
2235             mView.invalidate();
2236         }
2237     }
2238 
updateQsExpansion()2239     private void updateQsExpansion() {
2240         if (mQs == null) return;
2241         float qsExpansionFraction = computeQsExpansionFraction();
2242         mQs.setQsExpansion(qsExpansionFraction, getHeaderTranslation());
2243         mMediaHierarchyManager.setQsExpansion(qsExpansionFraction);
2244         int qsPanelBottomY = calculateQsBottomPosition(qsExpansionFraction);
2245         mScrimController.setQsPosition(qsExpansionFraction, qsPanelBottomY);
2246         setQSClippingBounds();
2247         mNotificationStackScrollLayoutController.setQsExpansionFraction(qsExpansionFraction);
2248         mDepthController.setQsPanelExpansion(qsExpansionFraction);
2249     }
2250 
onStackYChanged(boolean shouldAnimate)2251     private void onStackYChanged(boolean shouldAnimate) {
2252         if (mQs != null) {
2253             if (shouldAnimate) {
2254                 animateNextNotificationBounds(StackStateAnimator.ANIMATION_DURATION_STANDARD,
2255                         0 /* delay */);
2256                 mNotificationBoundsAnimationDelay = 0;
2257             }
2258             setQSClippingBounds();
2259         }
2260     };
2261 
onNotificationScrolled(int newScrollPosition)2262     private void onNotificationScrolled(int newScrollPosition) {
2263         updateQSExpansionEnabledAmbient();
2264     }
2265 
2266     @Override
setIsShadeOpening(boolean opening)2267     public void setIsShadeOpening(boolean opening) {
2268         mAmbientState.setIsShadeOpening(opening);
2269         updateQSExpansionEnabledAmbient();
2270     }
2271 
updateQSExpansionEnabledAmbient()2272     private void updateQSExpansionEnabledAmbient() {
2273         final float scrollRangeToTop = mAmbientState.getTopPadding() - mQuickQsOffsetHeight;
2274         mQsExpansionEnabledAmbient = mAmbientState.getScrollY() <= scrollRangeToTop;
2275         setQsExpansionEnabled();
2276     }
2277 
2278     /**
2279      * Updates scrim bounds, QS clipping, and KSV clipping as well based on the bounds of the shade
2280      * and QS state.
2281      */
setQSClippingBounds()2282     private void setQSClippingBounds() {
2283         int top;
2284         int bottom;
2285         int left;
2286         int right;
2287 
2288         final int qsPanelBottomY = calculateQsBottomPosition(computeQsExpansionFraction());
2289         final boolean qsVisible = (computeQsExpansionFraction() > 0 || qsPanelBottomY > 0);
2290 
2291         if (!mShouldUseSplitNotificationShade) {
2292             if (mTransitioningToFullShadeProgress > 0.0f) {
2293                 // If we're transitioning, let's use the actual value. The else case
2294                 // can be wrong during transitions when waiting for the keyguard to unlock
2295                 top = mTransitionToFullShadeQSPosition;
2296             } else {
2297                 final float notificationTop = getQSEdgePosition();
2298                 if (isOnKeyguard()) {
2299                     if (mKeyguardBypassController.getBypassEnabled()) {
2300                         // When bypassing on the keyguard, let's use the panel bottom.
2301                         // this should go away once we unify the stackY position and don't have
2302                         // to do this min anymore below.
2303                         top = qsPanelBottomY;
2304                     } else {
2305                         top = (int) Math.min(qsPanelBottomY, notificationTop);
2306                     }
2307                 } else {
2308                     top = (int) notificationTop;
2309                 }
2310             }
2311             top += mOverStretchAmount;
2312             bottom = getView().getBottom();
2313             // notification bounds should take full screen width regardless of insets
2314             left = 0;
2315             right = getView().getRight() + mDisplayRightInset;
2316         } else {
2317             top = Math.min(qsPanelBottomY, mSplitShadeNotificationsTopPadding);
2318             bottom = mNotificationStackScrollLayoutController.getHeight();
2319             left = mNotificationStackScrollLayoutController.getLeft();
2320             right = mNotificationStackScrollLayoutController.getRight();
2321         }
2322         // top should never be lower than bottom, otherwise it will be invisible.
2323         top = Math.min(top, bottom);
2324         applyQSClippingBounds(left, top, right, bottom, qsVisible);
2325     }
2326 
applyQSClippingBounds(int left, int top, int right, int bottom, boolean qsVisible)2327     private void applyQSClippingBounds(int left, int top, int right, int bottom,
2328             boolean qsVisible) {
2329         if (!mAnimateNextNotificationBounds || mKeyguardStatusAreaClipBounds.isEmpty()) {
2330             if (mQsClippingAnimation != null) {
2331                 // update the end position of the animator
2332                 mQsClippingAnimationEndBounds.set(left, top, right, bottom);
2333             } else {
2334                 applyQSClippingImmediately(left, top, right, bottom, qsVisible);
2335             }
2336         } else {
2337             mQsClippingAnimationEndBounds.set(left, top, right, bottom);
2338             final int startLeft = mKeyguardStatusAreaClipBounds.left;
2339             final int startTop = mKeyguardStatusAreaClipBounds.top;
2340             final int startRight = mKeyguardStatusAreaClipBounds.right;
2341             final int startBottom = mKeyguardStatusAreaClipBounds.bottom;
2342             if (mQsClippingAnimation != null) {
2343                 mQsClippingAnimation.cancel();
2344             }
2345             mQsClippingAnimation = ValueAnimator.ofFloat(0.0f, 1.0f);
2346             mQsClippingAnimation.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
2347             mQsClippingAnimation.setDuration(mNotificationBoundsAnimationDuration);
2348             mQsClippingAnimation.setStartDelay(mNotificationBoundsAnimationDelay);
2349             mQsClippingAnimation.addUpdateListener(animation -> {
2350                 float fraction = animation.getAnimatedFraction();
2351                 int animLeft = (int) MathUtils.lerp(startLeft,
2352                         mQsClippingAnimationEndBounds.left, fraction);
2353                 int animTop = (int) MathUtils.lerp(startTop,
2354                         mQsClippingAnimationEndBounds.top, fraction);
2355                 int animRight = (int) MathUtils.lerp(startRight,
2356                         mQsClippingAnimationEndBounds.right, fraction);
2357                 int animBottom = (int) MathUtils.lerp(startBottom,
2358                         mQsClippingAnimationEndBounds.bottom, fraction);
2359                 applyQSClippingImmediately(animLeft, animTop, animRight, animBottom,
2360                         qsVisible /* qsVisible */);
2361             });
2362             mQsClippingAnimation.addListener(new AnimatorListenerAdapter() {
2363                 @Override
2364                 public void onAnimationEnd(Animator animation) {
2365                     mQsClippingAnimation = null;
2366                     mIsQsTranslationResetAnimator = false;
2367                     mIsPulseExpansionResetAnimator = false;
2368                 }
2369             });
2370             mQsClippingAnimation.start();
2371         }
2372         mAnimateNextNotificationBounds = false;
2373         mNotificationBoundsAnimationDelay = 0;
2374     }
2375 
applyQSClippingImmediately(int left, int top, int right, int bottom, boolean qsVisible)2376     private void applyQSClippingImmediately(int left, int top, int right, int bottom,
2377             boolean qsVisible) {
2378         // Fancy clipping for quick settings
2379         int radius = mScrimCornerRadius;
2380         int statusBarClipTop = 0;
2381         boolean clipStatusView = false;
2382         if (!mShouldUseSplitNotificationShade) {
2383             // The padding on this area is large enough that we can use a cheaper clipping strategy
2384             mKeyguardStatusAreaClipBounds.set(left, top, right, bottom);
2385             clipStatusView = qsVisible;
2386             float screenCornerRadius = mRecordingController.isRecording() ? 0 : mScreenCornerRadius;
2387             radius = (int) MathUtils.lerp(screenCornerRadius, mScrimCornerRadius,
2388                     Math.min(top / (float) mScrimCornerRadius, 1f));
2389             statusBarClipTop = top - mKeyguardStatusBar.getTop();
2390         }
2391         if (mQs != null) {
2392             float qsTranslation = 0;
2393             boolean pulseExpanding = mPulseExpansionHandler.isExpanding();
2394             if (mTransitioningToFullShadeProgress > 0.0f || pulseExpanding
2395                     || (mQsClippingAnimation != null
2396                         && (mIsQsTranslationResetAnimator || mIsPulseExpansionResetAnimator))) {
2397                 if (pulseExpanding || mIsPulseExpansionResetAnimator) {
2398                     // qsTranslation should only be positive during pulse expansion because it's
2399                     // already translating in from the top
2400                     qsTranslation = Math.max(0, (top - mQs.getHeader().getHeight()) / 2.0f);
2401                 } else {
2402                     qsTranslation = (top - mQs.getHeader().getHeight()) * QS_PARALLAX_AMOUNT;
2403                 }
2404             }
2405             mQsTranslationForFullShadeTransition = qsTranslation;
2406             updateQsFrameTranslation();
2407             float currentTranslation = mQsFrame.getTranslationY();
2408             mQsClipTop = (int) (top - currentTranslation);
2409             mQsClipBottom = (int) (bottom - currentTranslation);
2410             mQsVisible = qsVisible;
2411             mQs.setFancyClipping(
2412                     mQsClipTop,
2413                     mQsClipBottom,
2414                     radius, qsVisible
2415                     && !mShouldUseSplitNotificationShade);
2416         }
2417         mKeyguardStatusViewController.setClipBounds(
2418                 clipStatusView ? mKeyguardStatusAreaClipBounds : null);
2419         if (!qsVisible && mShouldUseSplitNotificationShade) {
2420             // On the lockscreen when qs isn't visible, we don't want the bounds of the shade to
2421             // be visible, otherwise you can see the bounds once swiping up to see bouncer
2422             mScrimController.setNotificationsBounds(0, 0, 0, 0);
2423         } else {
2424             mScrimController.setNotificationsBounds(left, top, right, bottom);
2425         }
2426 
2427         mScrimController.setScrimCornerRadius(radius);
2428         mKeyguardStatusBar.setTopClipping(statusBarClipTop);
2429         int nsslLeft = left - mNotificationStackScrollLayoutController.getLeft();
2430         int nsslRight = right - mNotificationStackScrollLayoutController.getLeft();
2431         int nsslTop = top - mNotificationStackScrollLayoutController.getTop();
2432         int nsslBottom = bottom - mNotificationStackScrollLayoutController.getTop();
2433         int bottomRadius = mShouldUseSplitNotificationShade ? radius : 0;
2434         mNotificationStackScrollLayoutController.setRoundedClippingBounds(
2435                 nsslLeft, nsslTop, nsslRight, nsslBottom, radius, bottomRadius);
2436     }
2437 
getQSEdgePosition()2438     private float getQSEdgePosition() {
2439         // TODO: replace StackY with unified calculation
2440         return Math.max(mQuickQsOffsetHeight * mAmbientState.getExpansionFraction(),
2441                 mAmbientState.getStackY() - mAmbientState.getScrollY());
2442     }
2443 
calculateQsBottomPosition(float qsExpansionFraction)2444     private int calculateQsBottomPosition(float qsExpansionFraction) {
2445         if (mTransitioningToFullShadeProgress > 0.0f) {
2446             return mTransitionToFullShadeQSPosition;
2447         } else {
2448             int qsBottomY = (int) getHeaderTranslation() + mQs.getQsMinExpansionHeight();
2449             if (qsExpansionFraction != 0.0) {
2450                 qsBottomY = (int) MathUtils.lerp(
2451                         qsBottomY, mQs.getDesiredHeight(), qsExpansionFraction);
2452             }
2453             return qsBottomY;
2454         }
2455     }
2456 
determineAccessibilityPaneTitle()2457     private String determineAccessibilityPaneTitle() {
2458         if (mQs != null && mQs.isCustomizing()) {
2459             return mResources.getString(R.string.accessibility_desc_quick_settings_edit);
2460         } else if (mQsExpansionHeight != 0.0f && mQsFullyExpanded) {
2461             // Upon initialisation when we are not layouted yet we don't want to announce that we
2462             // are fully expanded, hence the != 0.0f check.
2463             return mResources.getString(R.string.accessibility_desc_quick_settings);
2464         } else if (mBarState == KEYGUARD) {
2465             return mResources.getString(R.string.accessibility_desc_lock_screen);
2466         } else {
2467             return mResources.getString(R.string.accessibility_desc_notification_shade);
2468         }
2469     }
2470 
calculateNotificationsTopPadding()2471     private float calculateNotificationsTopPadding() {
2472         if (mShouldUseSplitNotificationShade && !mKeyguardShowing) {
2473             return mSplitShadeNotificationsTopPadding;
2474         }
2475         if (mKeyguardShowing && (mQsExpandImmediate
2476                 || mIsExpanding && mQsExpandedWhenExpandingStarted)) {
2477 
2478             // Either QS pushes the notifications down when fully expanded, or QS is fully above the
2479             // notifications (mostly on tablets). maxNotificationPadding denotes the normal top
2480             // padding on Keyguard, maxQsPadding denotes the top padding from the quick settings
2481             // panel. We need to take the maximum and linearly interpolate with the panel expansion
2482             // for a nice motion.
2483             int maxNotificationPadding = getKeyguardNotificationStaticPadding();
2484             int maxQsPadding = mQsMaxExpansionHeight;
2485             int max = mBarState == KEYGUARD ? Math.max(
2486                     maxNotificationPadding, maxQsPadding) : maxQsPadding;
2487             return (int) MathUtils.lerp((float) mQsMinExpansionHeight, (float) max,
2488                     getExpandedFraction());
2489         } else if (mQsSizeChangeAnimator != null) {
2490             return Math.max(
2491                     (int) mQsSizeChangeAnimator.getAnimatedValue(),
2492                     getKeyguardNotificationStaticPadding());
2493         } else if (mKeyguardShowing) {
2494             // We can only do the smoother transition on Keyguard when we also are not collapsing
2495             // from a scrolled quick settings.
2496             return MathUtils.lerp((float) getKeyguardNotificationStaticPadding(),
2497                     (float) (mQsMaxExpansionHeight),
2498                     computeQsExpansionFraction());
2499         } else {
2500             return mQsExpansionHeight;
2501         }
2502     }
2503 
2504     /**
2505      * @return the topPadding of notifications when on keyguard not respecting quick settings
2506      * expansion
2507      */
getKeyguardNotificationStaticPadding()2508     private int getKeyguardNotificationStaticPadding() {
2509         if (!mKeyguardShowing) {
2510             return 0;
2511         }
2512         if (!mKeyguardBypassController.getBypassEnabled()) {
2513             return mClockPositionResult.stackScrollerPadding;
2514         }
2515         int collapsedPosition = mHeadsUpInset;
2516         if (!mNotificationStackScrollLayoutController.isPulseExpanding()) {
2517             return collapsedPosition;
2518         } else {
2519             int expandedPosition = mClockPositionResult.stackScrollerPadding;
2520             return (int) MathUtils.lerp(collapsedPosition, expandedPosition,
2521                     mNotificationStackScrollLayoutController.calculateAppearFractionBypass());
2522         }
2523     }
2524 
2525 
requestScrollerTopPaddingUpdate(boolean animate)2526     protected void requestScrollerTopPaddingUpdate(boolean animate) {
2527         mNotificationStackScrollLayoutController.updateTopPadding(
2528                 calculateNotificationsTopPadding(), animate);
2529         if (mKeyguardShowing && mKeyguardBypassController.getBypassEnabled()) {
2530             // update the position of the header
2531             updateQsExpansion();
2532         }
2533     }
2534 
2535     /**
2536      * Set the amount of pixels we have currently dragged down if we're transitioning to the full
2537      * shade. 0.0f means we're not transitioning yet.
2538      */
setTransitionToFullShadeAmount(float pxAmount, boolean animate, long delay)2539     public void setTransitionToFullShadeAmount(float pxAmount, boolean animate, long delay) {
2540         if (animate && !mShouldUseSplitNotificationShade) {
2541             animateNextNotificationBounds(StackStateAnimator.ANIMATION_DURATION_GO_TO_FULL_SHADE,
2542                     delay);
2543             mIsQsTranslationResetAnimator = mQsTranslationForFullShadeTransition > 0.0f;
2544         }
2545 
2546         float endPosition = 0;
2547         if (pxAmount > 0.0f) {
2548             if (mNotificationStackScrollLayoutController.getVisibleNotificationCount() == 0
2549                     && !mMediaDataManager.hasActiveMedia()) {
2550                 // No notifications are visible, let's animate to the height of qs instead
2551                 if (mQs != null) {
2552                     // Let's interpolate to the header height instead of the top padding,
2553                     // because the toppadding is way too low because of the large clock.
2554                     // we still want to take into account the edgePosition though as that nicely
2555                     // overshoots in the stackscroller
2556                     endPosition = getQSEdgePosition()
2557                             - mNotificationStackScrollLayoutController.getTopPadding()
2558                             + mQs.getHeader().getHeight();
2559                 }
2560             } else {
2561                 // Interpolating to the new bottom edge position!
2562                 endPosition = getQSEdgePosition()
2563                         + mNotificationStackScrollLayoutController.getFullShadeTransitionInset();
2564                 if (isOnKeyguard()) {
2565                     endPosition -= mLockscreenNotificationQSPadding;
2566                 }
2567             }
2568         }
2569 
2570         // Calculate the overshoot amount such that we're reaching the target after our desired
2571         // distance, but only reach it fully once we drag a full shade length.
2572         mTransitioningToFullShadeProgress = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(
2573                 MathUtils.saturate(pxAmount / mDistanceForQSFullShadeTransition));
2574 
2575         int position = (int) MathUtils.lerp((float) 0, endPosition,
2576                 mTransitioningToFullShadeProgress);
2577         if (mTransitioningToFullShadeProgress > 0.0f) {
2578             // we want at least 1 pixel otherwise the panel won't be clipped
2579             position = Math.max(1, position);
2580         }
2581         mTransitionToFullShadeQSPosition = position;
2582         updateQsExpansion();
2583     }
2584 
2585     /**
2586      * Notify the panel that the pulse expansion has finished and that we're going to the full
2587      * shade
2588      */
onPulseExpansionFinished()2589     public void onPulseExpansionFinished() {
2590         animateNextNotificationBounds(StackStateAnimator.ANIMATION_DURATION_GO_TO_FULL_SHADE, 0);
2591         mIsPulseExpansionResetAnimator = true;
2592     }
2593 
2594     /**
2595      * Set the alpha of the keyguard elements which only show on the lockscreen, but not in
2596      * shade locked / shade. This is used when dragging down to the full shade.
2597      */
setKeyguardOnlyContentAlpha(float keyguardAlpha)2598     public void setKeyguardOnlyContentAlpha(float keyguardAlpha) {
2599         mKeyguardOnlyContentAlpha = Interpolators.ALPHA_IN.getInterpolation(keyguardAlpha);
2600         if (mBarState == KEYGUARD) {
2601             // If the animator is running, it's already fading out the content and this is a reset
2602             mBottomAreaShadeAlpha = mKeyguardOnlyContentAlpha;
2603             updateKeyguardBottomAreaAlpha();
2604         }
2605         updateClock();
2606     }
2607 
trackMovement(MotionEvent event)2608     private void trackMovement(MotionEvent event) {
2609         if (mQsVelocityTracker != null) mQsVelocityTracker.addMovement(event);
2610     }
2611 
initVelocityTracker()2612     private void initVelocityTracker() {
2613         if (mQsVelocityTracker != null) {
2614             mQsVelocityTracker.recycle();
2615         }
2616         mQsVelocityTracker = VelocityTracker.obtain();
2617     }
2618 
getCurrentQSVelocity()2619     private float getCurrentQSVelocity() {
2620         if (mQsVelocityTracker == null) {
2621             return 0;
2622         }
2623         mQsVelocityTracker.computeCurrentVelocity(1000);
2624         return mQsVelocityTracker.getYVelocity();
2625     }
2626 
cancelQsAnimation()2627     private void cancelQsAnimation() {
2628         if (mQsExpansionAnimator != null) {
2629             mQsExpansionAnimator.cancel();
2630         }
2631     }
2632 
2633     /**
2634      * @see #flingSettings(float, int, Runnable, boolean)
2635      */
flingSettings(float vel, int type)2636     public void flingSettings(float vel, int type) {
2637         flingSettings(vel, type, null /* onFinishRunnable */, false /* isClick */);
2638     }
2639 
2640     /**
2641      * Animates QS or QQS as if the user had swiped up or down.
2642      *
2643      * @param vel              Finger velocity or 0 when not initiated by touch events.
2644      * @param type             Either {@link #FLING_EXPAND}, {@link #FLING_COLLAPSE} or {@link
2645      *                         #FLING_HIDE}.
2646      * @param onFinishRunnable Runnable to be executed at the end of animation.
2647      * @param isClick          If originated by click (different interpolator and duration.)
2648      */
flingSettings(float vel, int type, final Runnable onFinishRunnable, boolean isClick)2649     protected void flingSettings(float vel, int type, final Runnable onFinishRunnable,
2650             boolean isClick) {
2651         float target;
2652         switch (type) {
2653             case FLING_EXPAND:
2654                 target = mQsMaxExpansionHeight;
2655                 break;
2656             case FLING_COLLAPSE:
2657                 target = mQsMinExpansionHeight;
2658                 break;
2659             case FLING_HIDE:
2660             default:
2661                 if (mQs != null) {
2662                     mQs.closeDetail();
2663                 }
2664                 target = 0;
2665         }
2666         if (target == mQsExpansionHeight) {
2667             if (onFinishRunnable != null) {
2668                 onFinishRunnable.run();
2669             }
2670             traceQsJank(false /* startTracing */, type != FLING_EXPAND /* wasCancelled */);
2671             return;
2672         }
2673 
2674         // If we move in the opposite direction, reset velocity and use a different duration.
2675         boolean oppositeDirection = false;
2676         boolean expanding = type == FLING_EXPAND;
2677         if (vel > 0 && !expanding || vel < 0 && expanding) {
2678             vel = 0;
2679             oppositeDirection = true;
2680         }
2681         ValueAnimator animator = ValueAnimator.ofFloat(mQsExpansionHeight, target);
2682         if (isClick) {
2683             animator.setInterpolator(Interpolators.TOUCH_RESPONSE);
2684             animator.setDuration(368);
2685         } else {
2686             mFlingAnimationUtils.apply(animator, mQsExpansionHeight, target, vel);
2687         }
2688         if (oppositeDirection) {
2689             animator.setDuration(350);
2690         }
2691         animator.addUpdateListener(animation -> {
2692             setQsExpansion((Float) animation.getAnimatedValue());
2693         });
2694         animator.addListener(new AnimatorListenerAdapter() {
2695             private boolean mIsCanceled;
2696             @Override
2697             public void onAnimationStart(Animator animation) {
2698                 notifyExpandingStarted();
2699             }
2700 
2701             @Override
2702             public void onAnimationCancel(Animator animation) {
2703                 mIsCanceled = true;
2704             }
2705 
2706             @Override
2707             public void onAnimationEnd(Animator animation) {
2708                 mQSAnimatingHiddenFromCollapsed = false;
2709                 mAnimatingQS = false;
2710                 notifyExpandingFinished();
2711                 mNotificationStackScrollLayoutController.resetCheckSnoozeLeavebehind();
2712                 mQsExpansionAnimator = null;
2713                 if (onFinishRunnable != null) {
2714                     onFinishRunnable.run();
2715                 }
2716                 traceQsJank(false /* startTracing */, mIsCanceled /* wasCancelled */);
2717             }
2718         });
2719         // Let's note that we're animating QS. Moving the animator here will cancel it immediately,
2720         // so we need a separate flag.
2721         mAnimatingQS = true;
2722         animator.start();
2723         mQsExpansionAnimator = animator;
2724         mQsAnimatorExpand = expanding;
2725         mQSAnimatingHiddenFromCollapsed = computeQsExpansionFraction() == 0.0f && target == 0;
2726     }
2727 
2728     /**
2729      * @return Whether we should intercept a gesture to open Quick Settings.
2730      */
shouldQuickSettingsIntercept(float x, float y, float yDiff)2731     private boolean shouldQuickSettingsIntercept(float x, float y, float yDiff) {
2732         if (!isQsExpansionEnabled() || mCollapsedOnDown || (mKeyguardShowing
2733                 && mKeyguardBypassController.getBypassEnabled())) {
2734             return false;
2735         }
2736         View header = mKeyguardShowing || mQs == null ? mKeyguardStatusBar : mQs.getHeader();
2737 
2738         mQsInterceptRegion.set(
2739                 /* left= */ (int) mQsFrame.getX(),
2740                 /* top= */ header.getTop(),
2741                 /* right= */ (int) mQsFrame.getX() + mQsFrame.getWidth(),
2742                 /* bottom= */ header.getBottom());
2743         // Also allow QS to intercept if the touch is near the notch.
2744         mStatusBarTouchableRegionManager.updateRegionForNotch(mQsInterceptRegion);
2745         final boolean onHeader = mQsInterceptRegion.contains((int) x, (int) y);
2746 
2747         if (mQsExpanded) {
2748             return onHeader || (yDiff < 0 && isInQsArea(x, y));
2749         } else {
2750             return onHeader;
2751         }
2752     }
2753 
2754     @Override
canCollapsePanelOnTouch()2755     protected boolean canCollapsePanelOnTouch() {
2756         if (!isInSettings() && mBarState == KEYGUARD) {
2757             return true;
2758         }
2759 
2760         if (mNotificationStackScrollLayoutController.isScrolledToBottom()) {
2761             return true;
2762         }
2763 
2764         return !mShouldUseSplitNotificationShade && (isInSettings() || mIsPanelCollapseOnQQS);
2765     }
2766 
2767     @Override
getMaxPanelHeight()2768     protected int getMaxPanelHeight() {
2769         int min = mStatusBarMinHeight;
2770         if (!(mBarState == KEYGUARD)
2771                 && mNotificationStackScrollLayoutController.getNotGoneChildCount() == 0) {
2772             int minHeight = mQsMinExpansionHeight;
2773             min = Math.max(min, minHeight);
2774         }
2775         int maxHeight;
2776         if (mQsExpandImmediate || mQsExpanded || mIsExpanding && mQsExpandedWhenExpandingStarted
2777                 || mPulsing) {
2778             maxHeight = calculatePanelHeightQsExpanded();
2779         } else {
2780             maxHeight = calculatePanelHeightShade();
2781         }
2782         maxHeight = Math.max(min, maxHeight);
2783         if (maxHeight == 0 || isNaN(maxHeight)) {
2784             Log.wtf(TAG, "maxPanelHeight is invalid. mOverExpansion: "
2785                     + mOverExpansion + ", calculatePanelHeightQsExpanded: "
2786                     + calculatePanelHeightQsExpanded() + ", calculatePanelHeightShade: "
2787                     + calculatePanelHeightShade() + ", mStatusBarMinHeight = "
2788                     + mStatusBarMinHeight + ", mQsMinExpansionHeight = " + mQsMinExpansionHeight);
2789         }
2790         return maxHeight;
2791     }
2792 
isInSettings()2793     public boolean isInSettings() {
2794         return mQsExpanded;
2795     }
2796 
isExpanding()2797     public boolean isExpanding() {
2798         return mIsExpanding;
2799     }
2800 
2801     @Override
onHeightUpdated(float expandedHeight)2802     protected void onHeightUpdated(float expandedHeight) {
2803         if (!mQsExpanded || mQsExpandImmediate || mIsExpanding && mQsExpandedWhenExpandingStarted) {
2804             // Updating the clock position will set the top padding which might
2805             // trigger a new panel height and re-position the clock.
2806             // This is a circular dependency and should be avoided, otherwise we'll have
2807             // a stack overflow.
2808             if (mStackScrollerMeasuringPass > 2) {
2809                 if (DEBUG) Log.d(TAG, "Unstable notification panel height. Aborting.");
2810             } else {
2811                 positionClockAndNotifications();
2812             }
2813         }
2814         if (mQsExpandImmediate || mQsExpanded && !mQsTracking && mQsExpansionAnimator == null
2815                 && !mQsExpansionFromOverscroll) {
2816             float t;
2817             if (mKeyguardShowing) {
2818 
2819                 // On Keyguard, interpolate the QS expansion linearly to the panel expansion
2820                 t = expandedHeight / (getMaxPanelHeight());
2821             } else {
2822                 // In Shade, interpolate linearly such that QS is closed whenever panel height is
2823                 // minimum QS expansion + minStackHeight
2824                 float
2825                         panelHeightQsCollapsed =
2826                         mNotificationStackScrollLayoutController.getIntrinsicPadding()
2827                                 + mNotificationStackScrollLayoutController.getLayoutMinHeight();
2828                 float panelHeightQsExpanded = calculatePanelHeightQsExpanded();
2829                 t =
2830                         (expandedHeight - panelHeightQsCollapsed) / (panelHeightQsExpanded
2831                                 - panelHeightQsCollapsed);
2832             }
2833             float
2834                     targetHeight =
2835                     mQsMinExpansionHeight + t * (mQsMaxExpansionHeight - mQsMinExpansionHeight);
2836             setQsExpansion(targetHeight);
2837         }
2838         updateExpandedHeight(expandedHeight);
2839         updateHeader();
2840         updateNotificationTranslucency();
2841         updatePanelExpanded();
2842         updateGestureExclusionRect();
2843         if (DEBUG) {
2844             mView.invalidate();
2845         }
2846     }
2847 
updatePanelExpanded()2848     private void updatePanelExpanded() {
2849         boolean isExpanded = !isFullyCollapsed() || mExpectingSynthesizedDown;
2850         if (mPanelExpanded != isExpanded) {
2851             mHeadsUpManager.setIsPanelExpanded(isExpanded);
2852             mStatusBarTouchableRegionManager.setPanelExpanded(isExpanded);
2853             mStatusBar.setPanelExpanded(isExpanded);
2854             mPanelExpanded = isExpanded;
2855         }
2856     }
2857 
calculatePanelHeightShade()2858     private int calculatePanelHeightShade() {
2859         int emptyBottomMargin = mNotificationStackScrollLayoutController.getEmptyBottomMargin();
2860         int maxHeight = mNotificationStackScrollLayoutController.getHeight() - emptyBottomMargin;
2861 
2862         if (mBarState == KEYGUARD) {
2863             int minKeyguardPanelBottom = mClockPositionAlgorithm.getLockscreenStatusViewHeight()
2864                     + mNotificationStackScrollLayoutController.getIntrinsicContentHeight();
2865             return Math.max(maxHeight, minKeyguardPanelBottom);
2866         } else {
2867             return maxHeight;
2868         }
2869     }
2870 
calculatePanelHeightQsExpanded()2871     private int calculatePanelHeightQsExpanded() {
2872         float
2873                 notificationHeight =
2874                 mNotificationStackScrollLayoutController.getHeight()
2875                         - mNotificationStackScrollLayoutController.getEmptyBottomMargin()
2876                         - mNotificationStackScrollLayoutController.getTopPadding();
2877 
2878         // When only empty shade view is visible in QS collapsed state, simulate that we would have
2879         // it in expanded QS state as well so we don't run into troubles when fading the view in/out
2880         // and expanding/collapsing the whole panel from/to quick settings.
2881         if (mNotificationStackScrollLayoutController.getNotGoneChildCount() == 0
2882                 && mNotificationStackScrollLayoutController.isShowingEmptyShadeView()) {
2883             notificationHeight = mNotificationStackScrollLayoutController.getEmptyShadeViewHeight();
2884         }
2885         int maxQsHeight = mQsMaxExpansionHeight;
2886 
2887         // If an animation is changing the size of the QS panel, take the animated value.
2888         if (mQsSizeChangeAnimator != null) {
2889             maxQsHeight = (int) mQsSizeChangeAnimator.getAnimatedValue();
2890         }
2891         float totalHeight = Math.max(maxQsHeight,
2892                 mBarState == KEYGUARD ? mClockPositionResult.stackScrollerPadding
2893                         : 0) + notificationHeight
2894                 + mNotificationStackScrollLayoutController.getTopPaddingOverflow();
2895         if (totalHeight > mNotificationStackScrollLayoutController.getHeight()) {
2896             float
2897                     fullyCollapsedHeight =
2898                     maxQsHeight + mNotificationStackScrollLayoutController.getLayoutMinHeight();
2899             totalHeight = Math.max(fullyCollapsedHeight,
2900                     mNotificationStackScrollLayoutController.getHeight());
2901         }
2902         return (int) totalHeight;
2903     }
2904 
updateNotificationTranslucency()2905     private void updateNotificationTranslucency() {
2906         float alpha = 1f;
2907         if (mClosingWithAlphaFadeOut && !mExpandingFromHeadsUp
2908                 && !mHeadsUpManager.hasPinnedHeadsUp()) {
2909             alpha = getFadeoutAlpha();
2910         }
2911         if (mBarState == KEYGUARD && !mHintAnimationRunning
2912                 && !mKeyguardBypassController.getBypassEnabled()) {
2913             alpha *= mClockPositionResult.clockAlpha;
2914         }
2915         mNotificationStackScrollLayoutController.setAlpha(alpha);
2916     }
2917 
getFadeoutAlpha()2918     private float getFadeoutAlpha() {
2919         float alpha;
2920         if (mQsMinExpansionHeight == 0) {
2921             return 1.0f;
2922         }
2923         alpha = getExpandedHeight() / mQsMinExpansionHeight;
2924         alpha = Math.max(0, Math.min(alpha, 1));
2925         alpha = (float) Math.pow(alpha, 0.75);
2926         return alpha;
2927     }
2928 
2929     /**
2930      * Hides the header when notifications are colliding with it.
2931      */
updateHeader()2932     private void updateHeader() {
2933         if (mBarState == KEYGUARD) {
2934             updateHeaderKeyguardAlpha();
2935         }
2936         updateQsExpansion();
2937     }
2938 
getHeaderTranslation()2939     protected float getHeaderTranslation() {
2940         if (mBarState == KEYGUARD && !mKeyguardBypassController.getBypassEnabled()) {
2941             return -mQs.getQsMinExpansionHeight();
2942         }
2943         float appearAmount = mNotificationStackScrollLayoutController
2944                 .calculateAppearFraction(mExpandedHeight);
2945         float startHeight = -mQsExpansionHeight;
2946         if (!mShouldUseSplitNotificationShade && mBarState == StatusBarState.SHADE) {
2947             // Small parallax as we pull down and clip QS
2948             startHeight = -mQsExpansionHeight * QS_PARALLAX_AMOUNT;
2949         }
2950         if (mKeyguardBypassController.getBypassEnabled() && isOnKeyguard()) {
2951             appearAmount = mNotificationStackScrollLayoutController.calculateAppearFractionBypass();
2952             startHeight = -mQs.getQsMinExpansionHeight();
2953         }
2954         float translation = MathUtils.lerp(startHeight, 0, Math.min(1.0f, appearAmount));
2955         return Math.min(0, translation);
2956     }
2957 
2958     /**
2959      * @return the alpha to be used to fade out the contents on Keyguard (status bar, bottom area)
2960      * during swiping up
2961      */
getKeyguardContentsAlpha()2962     private float getKeyguardContentsAlpha() {
2963         float alpha;
2964         if (mBarState == KEYGUARD) {
2965 
2966             // When on Keyguard, we hide the header as soon as we expanded close enough to the
2967             // header
2968             alpha =
2969                     getExpandedHeight() / (mKeyguardStatusBar.getHeight()
2970                             + mNotificationsHeaderCollideDistance);
2971         } else {
2972 
2973             // In SHADE_LOCKED, the top card is already really close to the header. Hide it as
2974             // soon as we start translating the stack.
2975             alpha = getExpandedHeight() / mKeyguardStatusBar.getHeight();
2976         }
2977         alpha = MathUtils.saturate(alpha);
2978         alpha = (float) Math.pow(alpha, 0.75);
2979         return alpha;
2980     }
2981 
updateHeaderKeyguardAlpha()2982     private void updateHeaderKeyguardAlpha() {
2983         if (!mKeyguardShowing) {
2984             return;
2985         }
2986         float alphaQsExpansion = 1 - Math.min(1, computeQsExpansionFraction() * 2);
2987         float newAlpha = Math.min(getKeyguardContentsAlpha(), alphaQsExpansion)
2988                 * mKeyguardStatusBarAnimateAlpha;
2989         newAlpha *= 1.0f - mKeyguardHeadsUpShowingAmount;
2990         mKeyguardStatusBar.setAlpha(newAlpha);
2991         boolean
2992                 hideForBypass =
2993                 mFirstBypassAttempt && mUpdateMonitor.shouldListenForFace()
2994                         || mDelayShowingKeyguardStatusBar;
2995         mKeyguardStatusBar.setVisibility(
2996                 newAlpha != 0f && !mDozing && !hideForBypass ? View.VISIBLE : View.INVISIBLE);
2997     }
2998 
updateKeyguardBottomAreaAlpha()2999     private void updateKeyguardBottomAreaAlpha() {
3000         // There are two possible panel expansion behaviors:
3001         // • User dragging up to unlock: we want to fade out as quick as possible
3002         //   (ALPHA_EXPANSION_THRESHOLD) to avoid seeing the bouncer over the bottom area.
3003         // • User tapping on lock screen: bouncer won't be visible but panel expansion will
3004         //   change due to "unlock hint animation." In this case, fading out the bottom area
3005         //   would also hide the message that says "swipe to unlock," we don't want to do that.
3006         float expansionAlpha = MathUtils.map(
3007                 isUnlockHintRunning() ? 0 : KeyguardBouncer.ALPHA_EXPANSION_THRESHOLD, 1f, 0f, 1f,
3008                 getExpandedFraction());
3009         float alpha = Math.min(expansionAlpha, 1 - computeQsExpansionFraction());
3010         alpha *= mBottomAreaShadeAlpha;
3011         mKeyguardBottomArea.setAffordanceAlpha(alpha);
3012         mKeyguardBottomArea.setImportantForAccessibility(
3013                 alpha == 0f ? View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
3014                         : View.IMPORTANT_FOR_ACCESSIBILITY_AUTO);
3015         View ambientIndicationContainer = mStatusBar.getAmbientIndicationContainer();
3016         if (ambientIndicationContainer != null) {
3017             ambientIndicationContainer.setAlpha(alpha);
3018         }
3019         mLockIconViewController.setAlpha(alpha);
3020     }
3021 
3022     /**
3023      * Custom clock fades away when user drags up to unlock or pulls down quick settings.
3024      *
3025      * Updates alpha of custom clock to match the alpha of the KeyguardBottomArea. See
3026      * {@link #updateKeyguardBottomAreaAlpha}.
3027      */
updateBigClockAlpha()3028     private void updateBigClockAlpha() {
3029         float expansionAlpha = MathUtils.map(
3030                 isUnlockHintRunning() ? 0 : KeyguardBouncer.ALPHA_EXPANSION_THRESHOLD, 1f, 0f, 1f,
3031                 getExpandedFraction());
3032         float alpha = Math.min(expansionAlpha, 1 - computeQsExpansionFraction());
3033         mBigClockContainer.setAlpha(alpha);
3034     }
3035 
3036     @Override
onExpandingStarted()3037     protected void onExpandingStarted() {
3038         super.onExpandingStarted();
3039         mNotificationStackScrollLayoutController.onExpansionStarted();
3040         mIsExpanding = true;
3041         mQsExpandedWhenExpandingStarted = mQsFullyExpanded;
3042         mMediaHierarchyManager.setCollapsingShadeFromQS(mQsExpandedWhenExpandingStarted &&
3043                 /* We also start expanding when flinging closed Qs. Let's exclude that */
3044                 !mAnimatingQS);
3045         if (mQsExpanded) {
3046             onQsExpansionStarted();
3047         }
3048         // Since there are QS tiles in the header now, we need to make sure we start listening
3049         // immediately so they can be up to date.
3050         if (mQs == null) return;
3051         mQs.setHeaderListening(true);
3052     }
3053 
3054     @Override
onExpandingFinished()3055     protected void onExpandingFinished() {
3056         super.onExpandingFinished();
3057         mNotificationStackScrollLayoutController.onExpansionStopped();
3058         mHeadsUpManager.onExpandingFinished();
3059         mConversationNotificationManager.onNotificationPanelExpandStateChanged(isFullyCollapsed());
3060         mIsExpanding = false;
3061         mMediaHierarchyManager.setCollapsingShadeFromQS(false);
3062         mMediaHierarchyManager.setQsExpanded(mQsExpanded);
3063         if (isFullyCollapsed()) {
3064             DejankUtils.postAfterTraversal(new Runnable() {
3065                 @Override
3066                 public void run() {
3067                     setListening(false);
3068                 }
3069             });
3070 
3071             // Workaround b/22639032: Make sure we invalidate something because else RenderThread
3072             // thinks we are actually drawing a frame put in reality we don't, so RT doesn't go
3073             // ahead with rendering and we jank.
3074             mView.postOnAnimation(new Runnable() {
3075                 @Override
3076                 public void run() {
3077                     mView.getParent().invalidateChild(mView, M_DUMMY_DIRTY_RECT);
3078                 }
3079             });
3080         } else {
3081             setListening(true);
3082         }
3083         mQsExpandImmediate = false;
3084         mNotificationStackScrollLayoutController.setShouldShowShelfOnly(false);
3085         mTwoFingerQsExpandPossible = false;
3086         notifyListenersTrackingHeadsUp(null);
3087         mExpandingFromHeadsUp = false;
3088         setPanelScrimMinFraction(0.0f);
3089     }
3090 
notifyListenersTrackingHeadsUp(ExpandableNotificationRow pickedChild)3091     private void notifyListenersTrackingHeadsUp(ExpandableNotificationRow pickedChild) {
3092         for (int i = 0; i < mTrackingHeadsUpListeners.size(); i++) {
3093             Consumer<ExpandableNotificationRow> listener = mTrackingHeadsUpListeners.get(i);
3094             listener.accept(pickedChild);
3095         }
3096     }
3097 
setListening(boolean listening)3098     private void setListening(boolean listening) {
3099         mKeyguardStatusBar.setListening(listening);
3100         if (mQs == null) return;
3101         mQs.setListening(listening);
3102     }
3103 
3104     @Override
expand(boolean animate)3105     public void expand(boolean animate) {
3106         super.expand(animate);
3107         setListening(true);
3108     }
3109 
3110     @Override
setOverExpansion(float overExpansion)3111     public void setOverExpansion(float overExpansion) {
3112         if (overExpansion == mOverExpansion) {
3113             return;
3114         }
3115         super.setOverExpansion(overExpansion);
3116         // Translating the quick settings by half the overexpansion to center it in the background
3117         // frame
3118         updateQsFrameTranslation();
3119         mNotificationStackScrollLayoutController.setOverExpansion(overExpansion);
3120     }
3121 
updateQsFrameTranslation()3122     private void updateQsFrameTranslation() {
3123         float translation = mOverExpansion / 2.0f + mQsTranslationForFullShadeTransition;
3124         mQsFrame.setTranslationY(translation);
3125     }
3126 
3127     @Override
onTrackingStarted()3128     protected void onTrackingStarted() {
3129         mFalsingCollector.onTrackingStarted(!mKeyguardStateController.canDismissLockScreen());
3130         super.onTrackingStarted();
3131         if (mQsFullyExpanded) {
3132             mQsExpandImmediate = true;
3133             if (!mShouldUseSplitNotificationShade) {
3134                 mNotificationStackScrollLayoutController.setShouldShowShelfOnly(true);
3135             }
3136         }
3137         if (mBarState == KEYGUARD || mBarState == StatusBarState.SHADE_LOCKED) {
3138             mAffordanceHelper.animateHideLeftRightIcon();
3139         }
3140         mNotificationStackScrollLayoutController.onPanelTrackingStarted();
3141     }
3142 
3143     @Override
onTrackingStopped(boolean expand)3144     protected void onTrackingStopped(boolean expand) {
3145         mFalsingCollector.onTrackingStopped();
3146         super.onTrackingStopped(expand);
3147         if (expand) {
3148             mNotificationStackScrollLayoutController.setOverScrollAmount(0.0f, true /* onTop */,
3149                     true /* animate */);
3150         }
3151         mNotificationStackScrollLayoutController.onPanelTrackingStopped();
3152         if (expand && (mBarState == KEYGUARD
3153                 || mBarState == StatusBarState.SHADE_LOCKED)) {
3154             if (!mHintAnimationRunning) {
3155                 mAffordanceHelper.reset(true);
3156             }
3157         }
3158     }
3159 
updateMaxHeadsUpTranslation()3160     private void updateMaxHeadsUpTranslation() {
3161         mNotificationStackScrollLayoutController.setHeadsUpBoundaries(
3162                 getHeight(), mNavigationBarBottomHeight);
3163     }
3164 
3165     @Override
startUnlockHintAnimation()3166     protected void startUnlockHintAnimation() {
3167         if (mPowerManager.isPowerSaveMode()) {
3168             onUnlockHintStarted();
3169             onUnlockHintFinished();
3170             return;
3171         }
3172         super.startUnlockHintAnimation();
3173     }
3174 
3175     @Override
onUnlockHintFinished()3176     protected void onUnlockHintFinished() {
3177         super.onUnlockHintFinished();
3178         mNotificationStackScrollLayoutController.setUnlockHintRunning(false);
3179     }
3180 
3181     @Override
onUnlockHintStarted()3182     protected void onUnlockHintStarted() {
3183         super.onUnlockHintStarted();
3184         mNotificationStackScrollLayoutController.setUnlockHintRunning(true);
3185     }
3186 
3187     @Override
shouldUseDismissingAnimation()3188     protected boolean shouldUseDismissingAnimation() {
3189         return mBarState != StatusBarState.SHADE && (mKeyguardStateController.canDismissLockScreen()
3190                 || !isTracking());
3191     }
3192 
3193     @Override
isTrackingBlocked()3194     protected boolean isTrackingBlocked() {
3195         return mConflictingQsExpansionGesture && mQsExpanded || mBlockingExpansionForCurrentTouch;
3196     }
3197 
isQsExpanded()3198     public boolean isQsExpanded() {
3199         return mQsExpanded;
3200     }
3201 
isQsDetailShowing()3202     public boolean isQsDetailShowing() {
3203         return mQs.isShowingDetail();
3204     }
3205 
closeQsDetail()3206     public void closeQsDetail() {
3207         mQs.closeDetail();
3208     }
3209 
isLaunchTransitionFinished()3210     public boolean isLaunchTransitionFinished() {
3211         return mIsLaunchTransitionFinished;
3212     }
3213 
isLaunchTransitionRunning()3214     public boolean isLaunchTransitionRunning() {
3215         return mIsLaunchTransitionRunning;
3216     }
3217 
setLaunchTransitionEndRunnable(Runnable r)3218     public void setLaunchTransitionEndRunnable(Runnable r) {
3219         mLaunchAnimationEndRunnable = r;
3220     }
3221 
updateDozingVisibilities(boolean animate)3222     private void updateDozingVisibilities(boolean animate) {
3223         mKeyguardBottomArea.setDozing(mDozing, animate);
3224         if (!mDozing && animate) {
3225             animateKeyguardStatusBarIn(StackStateAnimator.ANIMATION_DURATION_STANDARD);
3226         }
3227     }
3228 
3229     @Override
isDozing()3230     public boolean isDozing() {
3231         return mDozing;
3232     }
3233 
setQsScrimEnabled(boolean qsScrimEnabled)3234     public void setQsScrimEnabled(boolean qsScrimEnabled) {
3235         boolean changed = mQsScrimEnabled != qsScrimEnabled;
3236         mQsScrimEnabled = qsScrimEnabled;
3237         if (changed) {
3238             updateQsState();
3239         }
3240     }
3241 
onScreenTurningOn()3242     public void onScreenTurningOn() {
3243         mKeyguardStatusViewController.dozeTimeTick();
3244     }
3245 
3246     @Override
onMiddleClicked()3247     protected boolean onMiddleClicked() {
3248         switch (mBarState) {
3249             case KEYGUARD:
3250                 if (!mDozingOnDown) {
3251                     if (mUpdateMonitor.isFaceEnrolled()
3252                             && !mUpdateMonitor.isFaceDetectionRunning()
3253                             && !mUpdateMonitor.getUserCanSkipBouncer(
3254                                     KeyguardUpdateMonitor.getCurrentUser())) {
3255                         mUpdateMonitor.requestFaceAuth(true);
3256                     } else {
3257                         mLockscreenGestureLogger.write(MetricsEvent.ACTION_LS_HINT,
3258                                 0 /* lengthDp - N/A */, 0 /* velocityDp - N/A */);
3259                         mLockscreenGestureLogger
3260                             .log(LockscreenUiEvent.LOCKSCREEN_LOCK_SHOW_HINT);
3261                         startUnlockHintAnimation();
3262                     }
3263                 }
3264                 return true;
3265             case StatusBarState.SHADE_LOCKED:
3266                 if (!mQsExpanded) {
3267                     mStatusBarStateController.setState(KEYGUARD);
3268                 }
3269                 return true;
3270             case StatusBarState.SHADE:
3271 
3272                 // This gets called in the middle of the touch handling, where the state is still
3273                 // that we are tracking the panel. Collapse the panel after this is done.
3274                 mView.post(mPostCollapseRunnable);
3275                 return false;
3276             default:
3277                 return true;
3278         }
3279     }
3280 
setPanelAlpha(int alpha, boolean animate)3281     public void setPanelAlpha(int alpha, boolean animate) {
3282         if (mPanelAlpha != alpha) {
3283             mPanelAlpha = alpha;
3284             PropertyAnimator.setProperty(mView, mPanelAlphaAnimator, alpha, alpha == 255
3285                             ? mPanelAlphaInPropertiesAnimator : mPanelAlphaOutPropertiesAnimator,
3286                     animate);
3287         }
3288     }
3289 
setPanelAlphaEndAction(Runnable r)3290     public void setPanelAlphaEndAction(Runnable r) {
3291         mPanelAlphaEndAction = r;
3292     }
3293 
updateKeyguardStatusBarForHeadsUp()3294     private void updateKeyguardStatusBarForHeadsUp() {
3295         boolean
3296                 showingKeyguardHeadsUp =
3297                 mKeyguardShowing && mHeadsUpAppearanceController.shouldBeVisible();
3298         if (mShowingKeyguardHeadsUp != showingKeyguardHeadsUp) {
3299             mShowingKeyguardHeadsUp = showingKeyguardHeadsUp;
3300             if (mKeyguardShowing) {
3301                 PropertyAnimator.setProperty(mView, KEYGUARD_HEADS_UP_SHOWING_AMOUNT,
3302                         showingKeyguardHeadsUp ? 1.0f : 0.0f, KEYGUARD_HUN_PROPERTIES,
3303                         true /* animate */);
3304             } else {
3305                 PropertyAnimator.applyImmediately(mView, KEYGUARD_HEADS_UP_SHOWING_AMOUNT, 0.0f);
3306             }
3307         }
3308     }
3309 
setKeyguardHeadsUpShowingAmount(float amount)3310     private void setKeyguardHeadsUpShowingAmount(float amount) {
3311         mKeyguardHeadsUpShowingAmount = amount;
3312         updateHeaderKeyguardAlpha();
3313     }
3314 
getKeyguardHeadsUpShowingAmount()3315     private float getKeyguardHeadsUpShowingAmount() {
3316         return mKeyguardHeadsUpShowingAmount;
3317     }
3318 
setHeadsUpAnimatingAway(boolean headsUpAnimatingAway)3319     public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) {
3320         mHeadsUpAnimatingAway = headsUpAnimatingAway;
3321         mNotificationStackScrollLayoutController.setHeadsUpAnimatingAway(headsUpAnimatingAway);
3322         updateHeadsUpVisibility();
3323     }
3324 
updateHeadsUpVisibility()3325     private void updateHeadsUpVisibility() {
3326         ((PhoneStatusBarView) mBar).setHeadsUpVisible(mHeadsUpAnimatingAway || mHeadsUpPinnedMode);
3327     }
3328 
3329     @Override
setHeadsUpManager(HeadsUpManagerPhone headsUpManager)3330     public void setHeadsUpManager(HeadsUpManagerPhone headsUpManager) {
3331         super.setHeadsUpManager(headsUpManager);
3332         mHeadsUpTouchHelper = new HeadsUpTouchHelper(headsUpManager,
3333                 mNotificationStackScrollLayoutController.getHeadsUpCallback(),
3334                 NotificationPanelViewController.this);
3335     }
3336 
setTrackedHeadsUp(ExpandableNotificationRow pickedChild)3337     public void setTrackedHeadsUp(ExpandableNotificationRow pickedChild) {
3338         if (pickedChild != null) {
3339             notifyListenersTrackingHeadsUp(pickedChild);
3340             mExpandingFromHeadsUp = true;
3341         }
3342         // otherwise we update the state when the expansion is finished
3343     }
3344 
3345     @Override
onClosingFinished()3346     protected void onClosingFinished() {
3347         super.onClosingFinished();
3348         resetHorizontalPanelPosition();
3349         setClosingWithAlphaFadeout(false);
3350         mMediaHierarchyManager.closeGuts();
3351     }
3352 
setClosingWithAlphaFadeout(boolean closing)3353     private void setClosingWithAlphaFadeout(boolean closing) {
3354         mClosingWithAlphaFadeOut = closing;
3355         mNotificationStackScrollLayoutController.forceNoOverlappingRendering(closing);
3356     }
3357 
3358     /**
3359      * Updates the horizontal position of the panel so it is positioned closer to the touch
3360      * responsible for opening the panel.
3361      *
3362      * @param x the x-coordinate the touch event
3363      */
updateHorizontalPanelPosition(float x)3364     protected void updateHorizontalPanelPosition(float x) {
3365         if (mNotificationStackScrollLayoutController.getWidth() * 1.75f > mView.getWidth()
3366                 || mShouldUseSplitNotificationShade) {
3367             resetHorizontalPanelPosition();
3368             return;
3369         }
3370         float leftMost = mPositionMinSideMargin
3371                 + mNotificationStackScrollLayoutController.getWidth() / 2;
3372         float
3373                 rightMost =
3374                 mView.getWidth() - mPositionMinSideMargin
3375                         - mNotificationStackScrollLayoutController.getWidth() / 2;
3376         if (Math.abs(x - mView.getWidth() / 2)
3377                 < mNotificationStackScrollLayoutController.getWidth() / 4) {
3378             x = mView.getWidth() / 2;
3379         }
3380         x = Math.min(rightMost, Math.max(leftMost, x));
3381         float
3382                 center = mNotificationStackScrollLayoutController.getLeft()
3383                 + mNotificationStackScrollLayoutController.getWidth() / 2;
3384         setHorizontalPanelTranslation(x - center);
3385     }
3386 
resetHorizontalPanelPosition()3387     private void resetHorizontalPanelPosition() {
3388         setHorizontalPanelTranslation(0f);
3389     }
3390 
setHorizontalPanelTranslation(float translation)3391     protected void setHorizontalPanelTranslation(float translation) {
3392         mNotificationStackScrollLayoutController.setTranslationX(translation);
3393         mQsFrame.setTranslationX(translation);
3394         if (mVerticalTranslationListener != null) {
3395             mVerticalTranslationListener.run();
3396         }
3397     }
3398 
updateExpandedHeight(float expandedHeight)3399     protected void updateExpandedHeight(float expandedHeight) {
3400         if (mTracking) {
3401             mNotificationStackScrollLayoutController
3402                     .setExpandingVelocity(getCurrentExpandVelocity());
3403         }
3404         if (mKeyguardBypassController.getBypassEnabled() && isOnKeyguard()) {
3405             // The expandedHeight is always the full panel Height when bypassing
3406             expandedHeight = getMaxPanelHeight();
3407         }
3408         mNotificationStackScrollLayoutController.setExpandedHeight(expandedHeight);
3409         updateKeyguardBottomAreaAlpha();
3410         updateBigClockAlpha();
3411         updateStatusBarIcons();
3412     }
3413 
3414     /**
3415      * @return whether the notifications are displayed full width and don't have any margins on
3416      * the side.
3417      */
isFullWidth()3418     public boolean isFullWidth() {
3419         return mIsFullWidth;
3420     }
3421 
updateStatusBarIcons()3422     private void updateStatusBarIcons() {
3423         boolean
3424                 showIconsWhenExpanded =
3425                 (isPanelVisibleBecauseOfHeadsUp() || isFullWidth())
3426                         && getExpandedHeight() < getOpeningHeight();
3427         boolean noVisibleNotifications = true;
3428         if (showIconsWhenExpanded && noVisibleNotifications && isOnKeyguard()) {
3429             showIconsWhenExpanded = false;
3430         }
3431         if (showIconsWhenExpanded != mShowIconsWhenExpanded) {
3432             mShowIconsWhenExpanded = showIconsWhenExpanded;
3433             mCommandQueue.recomputeDisableFlags(mDisplayId, false);
3434         }
3435     }
3436 
3437     private boolean isOnKeyguard() {
3438         return mBarState == KEYGUARD;
3439     }
3440 
3441     public void setPanelScrimMinFraction(float minFraction) {
3442         mBar.panelScrimMinFractionChanged(minFraction);
3443     }
3444 
3445     public void clearNotificationEffects() {
3446         mStatusBar.clearNotificationEffects();
3447     }
3448 
3449     @Override
3450     protected boolean isPanelVisibleBecauseOfHeadsUp() {
3451         return (mHeadsUpManager.hasPinnedHeadsUp() || mHeadsUpAnimatingAway)
3452                 && mBarState == StatusBarState.SHADE;
3453     }
3454 
3455     public void launchCamera(boolean animate, int source) {
3456         if (source == StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP) {
3457             mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP;
3458         } else if (source == StatusBarManager.CAMERA_LAUNCH_SOURCE_WIGGLE) {
3459             mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_WIGGLE;
3460         } else if (source == StatusBarManager.CAMERA_LAUNCH_SOURCE_LIFT_TRIGGER) {
3461             mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_LIFT_TRIGGER;
3462         } else {
3463 
3464             // Default.
3465             mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_AFFORDANCE;
3466         }
3467 
3468         // If we are launching it when we are occluded already we don't want it to animate,
3469         // nor setting these flags, since the occluded state doesn't change anymore, hence it's
3470         // never reset.
3471         if (!isFullyCollapsed()) {
3472             setLaunchingAffordance(true);
3473         } else {
3474             animate = false;
3475         }
3476         mAffordanceHasPreview = mKeyguardBottomArea.getRightPreview() != null;
3477         mAffordanceHelper.launchAffordance(
3478                 animate, mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL);
3479     }
3480 
3481     public void onAffordanceLaunchEnded() {
3482         setLaunchingAffordance(false);
3483     }
3484 
3485     /**
3486      * Set whether we are currently launching an affordance. This is currently only set when
3487      * launched via a camera gesture.
3488      */
3489     private void setLaunchingAffordance(boolean launchingAffordance) {
3490         mLaunchingAffordance = launchingAffordance;
3491         mKeyguardAffordanceHelperCallback.getLeftIcon().setLaunchingAffordance(launchingAffordance);
3492         mKeyguardAffordanceHelperCallback.getRightIcon().setLaunchingAffordance(
3493                 launchingAffordance);
3494         mKeyguardBypassController.setLaunchingAffordance(launchingAffordance);
3495     }
3496 
3497     /**
3498      * Return true when a bottom affordance is launching an occluded activity with a splash screen.
3499      */
3500     public boolean isLaunchingAffordanceWithPreview() {
3501         return mLaunchingAffordance && mAffordanceHasPreview;
3502     }
3503 
3504     /**
3505      * Whether the camera application can be launched for the camera launch gesture.
3506      */
3507     public boolean canCameraGestureBeLaunched() {
3508         if (!mStatusBar.isCameraAllowedByAdmin()) {
3509             return false;
3510         }
3511 
3512         ResolveInfo resolveInfo = mKeyguardBottomArea.resolveCameraIntent();
3513         String
3514                 packageToLaunch =
3515                 (resolveInfo == null || resolveInfo.activityInfo == null) ? null
3516                         : resolveInfo.activityInfo.packageName;
3517         return packageToLaunch != null && (mBarState != StatusBarState.SHADE || !isForegroundApp(
3518                 packageToLaunch)) && !mAffordanceHelper.isSwipingInProgress();
3519     }
3520 
3521     /**
3522      * Return true if the applications with the package name is running in foreground.
3523      *
3524      * @param pkgName application package name.
3525      */
3526     private boolean isForegroundApp(String pkgName) {
3527         List<ActivityManager.RunningTaskInfo> tasks = mActivityManager.getRunningTasks(1);
3528         return !tasks.isEmpty() && pkgName.equals(tasks.get(0).topActivity.getPackageName());
3529     }
3530 
3531     public boolean hideStatusBarIconsWhenExpanded() {
3532         if (mIsLaunchAnimationRunning) {
3533             return mHideIconsDuringLaunchAnimation;
3534         }
3535         if (mHeadsUpAppearanceController != null
3536                 && mHeadsUpAppearanceController.shouldBeVisible()) {
3537             return false;
3538         }
3539         return !isFullWidth() || !mShowIconsWhenExpanded;
3540     }
3541 
3542     public final QS.ScrollListener mScrollListener = scrollY -> {
3543         if (scrollY > 0 && !mQsFullyExpanded) {
3544             if (DEBUG) Log.d(TAG, "Scrolling while not expanded. Forcing expand");
3545             // If we are scrolling QS, we should be fully expanded.
3546             expandWithQs();
3547         }
3548     };
3549 
3550     private final FragmentListener mFragmentListener = new FragmentListener() {
3551         @Override
3552         public void onFragmentViewCreated(String tag, Fragment fragment) {
3553             mQs = (QS) fragment;
3554             mQs.setPanelView(mHeightListener);
3555             mQs.setExpandClickListener(mOnClickListener);
3556             mQs.setHeaderClickable(isQsExpansionEnabled());
3557             mQs.setOverscrolling(mStackScrollerOverscrolling);
3558             mQs.setTranslateWhileExpanding(mShouldUseSplitNotificationShade);
3559 
3560             // recompute internal state when qspanel height changes
3561             mQs.getView().addOnLayoutChangeListener(
3562                     (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
3563                         final int height = bottom - top;
3564                         final int oldHeight = oldBottom - oldTop;
3565                         if (height != oldHeight) {
3566                             mHeightListener.onQsHeightChanged();
3567                         }
3568                     });
3569             mQs.setCollapsedMediaVisibilityChangedListener((visible) -> {
3570                 if (mQs.getHeader().isShown()) {
3571                     animateNextNotificationBounds(StackStateAnimator.ANIMATION_DURATION_STANDARD,
3572                             0 /* delay */);
3573                     mNotificationStackScrollLayoutController.animateNextTopPaddingChange();
3574                 }
3575             });
3576             mLockscreenShadeTransitionController.setQS(mQs);
3577             mNotificationStackScrollLayoutController.setQsContainer((ViewGroup) mQs.getView());
3578             mQs.setScrollListener(mScrollListener);
3579             updateQsExpansion();
3580         }
3581 
3582         @Override
3583         public void onFragmentViewDestroyed(String tag, Fragment fragment) {
3584             // Manual handling of fragment lifecycle is only required because this bridges
3585             // non-fragment and fragment code. Once we are using a fragment for the notification
3586             // panel, mQs will not need to be null cause it will be tied to the same lifecycle.
3587             if (fragment == mQs) {
3588                 mQs = null;
3589             }
3590         }
3591     };
3592 
animateNextNotificationBounds(long duration, long delay)3593     private void animateNextNotificationBounds(long duration, long delay) {
3594         mAnimateNextNotificationBounds = true;
3595         mNotificationBoundsAnimationDuration = duration;
3596         mNotificationBoundsAnimationDelay = delay;
3597     }
3598 
3599     @Override
setTouchAndAnimationDisabled(boolean disabled)3600     public void setTouchAndAnimationDisabled(boolean disabled) {
3601         super.setTouchAndAnimationDisabled(disabled);
3602         if (disabled && mAffordanceHelper.isSwipingInProgress() && !mIsLaunchTransitionRunning) {
3603             mAffordanceHelper.reset(false /* animate */);
3604         }
3605         mNotificationStackScrollLayoutController.setAnimationsEnabled(!disabled);
3606     }
3607 
3608     /**
3609      * Sets the dozing state.
3610      *
3611      * @param dozing              {@code true} when dozing.
3612      * @param animate             if transition should be animated.
3613      * @param wakeUpTouchLocation touch event location - if woken up by SLPI sensor.
3614      */
setDozing(boolean dozing, boolean animate, PointF wakeUpTouchLocation)3615     public void setDozing(boolean dozing, boolean animate, PointF wakeUpTouchLocation) {
3616         if (dozing == mDozing) return;
3617         mView.setDozing(dozing);
3618         mDozing = dozing;
3619         mNotificationStackScrollLayoutController.setDozing(mDozing, animate, wakeUpTouchLocation);
3620         mKeyguardBottomArea.setDozing(mDozing, animate);
3621 
3622         if (dozing) {
3623             mBottomAreaShadeAlphaAnimator.cancel();
3624         }
3625 
3626         if (mBarState == KEYGUARD || mBarState == StatusBarState.SHADE_LOCKED) {
3627             updateDozingVisibilities(animate);
3628         }
3629 
3630         final float dozeAmount = dozing ? 1 : 0;
3631         mStatusBarStateController.setAndInstrumentDozeAmount(mView, dozeAmount, animate);
3632     }
3633 
setPulsing(boolean pulsing)3634     public void setPulsing(boolean pulsing) {
3635         mPulsing = pulsing;
3636         final boolean
3637                 animatePulse =
3638                 !mDozeParameters.getDisplayNeedsBlanking() && mDozeParameters.getAlwaysOn();
3639         if (animatePulse) {
3640             mAnimateNextPositionUpdate = true;
3641         }
3642         // Do not animate the clock when waking up from a pulse.
3643         // The height callback will take care of pushing the clock to the right position.
3644         if (!mPulsing && !mDozing) {
3645             mAnimateNextPositionUpdate = false;
3646         }
3647         mNotificationStackScrollLayoutController.setPulsing(pulsing, animatePulse);
3648     }
3649 
setAmbientIndicationBottomPadding(int ambientIndicationBottomPadding)3650     public void setAmbientIndicationBottomPadding(int ambientIndicationBottomPadding) {
3651         if (mAmbientIndicationBottomPadding != ambientIndicationBottomPadding) {
3652             mAmbientIndicationBottomPadding = ambientIndicationBottomPadding;
3653             updateMaxDisplayedNotifications(true);
3654         }
3655     }
3656 
dozeTimeTick()3657     public void dozeTimeTick() {
3658         mKeyguardBottomArea.dozeTimeTick();
3659         mKeyguardStatusViewController.dozeTimeTick();
3660         if (mInterpolatedDarkAmount > 0) {
3661             positionClockAndNotifications();
3662         }
3663     }
3664 
setStatusAccessibilityImportance(int mode)3665     public void setStatusAccessibilityImportance(int mode) {
3666         mKeyguardStatusViewController.setStatusAccessibilityImportance(mode);
3667     }
3668 
3669     /**
3670      * TODO: this should be removed.
3671      * It's not correct to pass this view forward because other classes will end up adding
3672      * children to it. Theme will be out of sync.
3673      *
3674      * @return bottom area view
3675      */
getKeyguardBottomAreaView()3676     public KeyguardBottomAreaView getKeyguardBottomAreaView() {
3677         return mKeyguardBottomArea;
3678     }
3679 
setUserSetupComplete(boolean userSetupComplete)3680     public void setUserSetupComplete(boolean userSetupComplete) {
3681         mUserSetupComplete = userSetupComplete;
3682         mKeyguardBottomArea.setUserSetupComplete(userSetupComplete);
3683     }
3684 
applyLaunchAnimationProgress(float linearProgress)3685     public void applyLaunchAnimationProgress(float linearProgress) {
3686         boolean hideIcons = ActivityLaunchAnimator.getProgress(linearProgress,
3687                 ANIMATION_DELAY_ICON_FADE_IN, 100) == 0.0f;
3688         if (hideIcons != mHideIconsDuringLaunchAnimation) {
3689             mHideIconsDuringLaunchAnimation = hideIcons;
3690             if (!hideIcons) {
3691                 mCommandQueue.recomputeDisableFlags(mDisplayId, true /* animate */);
3692             }
3693         }
3694     }
3695 
addTrackingHeadsUpListener(Consumer<ExpandableNotificationRow> listener)3696     public void addTrackingHeadsUpListener(Consumer<ExpandableNotificationRow> listener) {
3697         mTrackingHeadsUpListeners.add(listener);
3698     }
3699 
removeTrackingHeadsUpListener(Consumer<ExpandableNotificationRow> listener)3700     public void removeTrackingHeadsUpListener(Consumer<ExpandableNotificationRow> listener) {
3701         mTrackingHeadsUpListeners.remove(listener);
3702     }
3703 
setVerticalTranslationListener(Runnable verticalTranslationListener)3704     public void setVerticalTranslationListener(Runnable verticalTranslationListener) {
3705         mVerticalTranslationListener = verticalTranslationListener;
3706     }
3707 
setHeadsUpAppearanceController( HeadsUpAppearanceController headsUpAppearanceController)3708     public void setHeadsUpAppearanceController(
3709             HeadsUpAppearanceController headsUpAppearanceController) {
3710         mHeadsUpAppearanceController = headsUpAppearanceController;
3711     }
3712 
3713     /**
3714      * Starts the animation before we dismiss Keyguard, i.e. an disappearing animation on the
3715      * security view of the bouncer.
3716      */
onBouncerPreHideAnimation()3717     public void onBouncerPreHideAnimation() {
3718         if (mKeyguardQsUserSwitchController != null) {
3719             mKeyguardQsUserSwitchController.setKeyguardQsUserSwitchVisibility(
3720                     mBarState,
3721                     true /* keyguardFadingAway */,
3722                     false /* goingToFullShade */,
3723                     mBarState);
3724         }
3725         if (mKeyguardUserSwitcherController != null) {
3726             mKeyguardUserSwitcherController.setKeyguardUserSwitcherVisibility(
3727                     mBarState,
3728                     true /* keyguardFadingAway */,
3729                     false /* goingToFullShade */,
3730                     mBarState);
3731         }
3732     }
3733 
3734     /**
3735      * Do not let the user drag the shade up and down for the current touch session.
3736      * This is necessary to avoid shade expansion while/after the bouncer is dismissed.
3737      */
blockExpansionForCurrentTouch()3738     public void blockExpansionForCurrentTouch() {
3739         mBlockingExpansionForCurrentTouch = mTracking;
3740     }
3741 
3742     @Override
dump(FileDescriptor fd, PrintWriter pw, String[] args)3743     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
3744         super.dump(fd, pw, args);
3745         pw.println("    gestureExclusionRect: " + calculateGestureExclusionRect()
3746                 + " applyQSClippingImmediately: top(" + mQsClipTop + ") bottom(" + mQsClipBottom
3747                 + ") qsVisible(" + mQsVisible
3748         );
3749         if (mKeyguardStatusBar != null) {
3750             mKeyguardStatusBar.dump(fd, pw, args);
3751         }
3752     }
3753 
hasActiveClearableNotifications()3754     public boolean hasActiveClearableNotifications() {
3755         return mNotificationStackScrollLayoutController.hasActiveClearableNotifications(ROWS_ALL);
3756     }
3757 
createRemoteInputDelegate()3758     public RemoteInputController.Delegate createRemoteInputDelegate() {
3759         return mNotificationStackScrollLayoutController.createDelegate();
3760     }
3761 
3762     /**
3763      * Updates the notification views' sections and status bar icons. This is
3764      * triggered by the NotificationPresenter whenever there are changes to the underlying
3765      * notification data being displayed. In the new notification pipeline, this is handled in
3766      * {@link ShadeViewManager}.
3767      */
updateNotificationViews(String reason)3768     public void updateNotificationViews(String reason) {
3769         mNotificationStackScrollLayoutController.updateSectionBoundaries(reason);
3770         mNotificationStackScrollLayoutController.updateFooter();
3771 
3772         mNotificationIconAreaController.updateNotificationIcons(createVisibleEntriesList());
3773     }
3774 
createVisibleEntriesList()3775     private List<ListEntry> createVisibleEntriesList() {
3776         List<ListEntry> entries = new ArrayList<>(
3777                 mNotificationStackScrollLayoutController.getChildCount());
3778         for (int i = 0; i < mNotificationStackScrollLayoutController.getChildCount(); i++) {
3779             View view = mNotificationStackScrollLayoutController.getChildAt(i);
3780             if (view instanceof ExpandableNotificationRow) {
3781                 entries.add(((ExpandableNotificationRow) view).getEntry());
3782             }
3783         }
3784         return entries;
3785     }
3786 
onUpdateRowStates()3787     public void onUpdateRowStates() {
3788         mNotificationStackScrollLayoutController.onUpdateRowStates();
3789     }
3790 
hasPulsingNotifications()3791     public boolean hasPulsingNotifications() {
3792         return mNotificationStackScrollLayoutController
3793                 .getNotificationListContainer().hasPulsingNotifications();
3794     }
3795 
getActivatedChild()3796     public ActivatableNotificationView getActivatedChild() {
3797         return mNotificationStackScrollLayoutController.getActivatedChild();
3798     }
3799 
setActivatedChild(ActivatableNotificationView o)3800     public void setActivatedChild(ActivatableNotificationView o) {
3801         mNotificationStackScrollLayoutController.setActivatedChild(o);
3802     }
3803 
runAfterAnimationFinished(Runnable r)3804     public void runAfterAnimationFinished(Runnable r) {
3805         mNotificationStackScrollLayoutController.runAfterAnimationFinished(r);
3806     }
3807 
setScrollingEnabled(boolean b)3808     public void setScrollingEnabled(boolean b) {
3809         mNotificationStackScrollLayoutController.setScrollingEnabled(b);
3810     }
3811 
3812     /**
3813      * Initialize objects instead of injecting to avoid circular dependencies.
3814      */
initDependencies( StatusBar statusBar, NotificationShelfController notificationShelfController)3815     public void initDependencies(
3816             StatusBar statusBar,
3817             NotificationShelfController notificationShelfController) {
3818         setStatusBar(statusBar);
3819         mNotificationStackScrollLayoutController.setShelfController(notificationShelfController);
3820         mNotificationShelfController = notificationShelfController;
3821         mLockscreenShadeTransitionController.bindController(notificationShelfController);
3822         updateMaxDisplayedNotifications(true);
3823     }
3824 
setAlpha(float alpha)3825     public void setAlpha(float alpha) {
3826         mView.setAlpha(alpha);
3827     }
3828 
fadeOut(long startDelayMs, long durationMs, Runnable endAction)3829     public ViewPropertyAnimator fadeOut(long startDelayMs, long durationMs, Runnable endAction) {
3830         return mView.animate().alpha(0).setStartDelay(startDelayMs).setDuration(
3831                 durationMs).setInterpolator(Interpolators.ALPHA_OUT).withLayer().withEndAction(
3832                 endAction);
3833     }
3834 
resetViewGroupFade()3835     public void resetViewGroupFade() {
3836         ViewGroupFadeHelper.reset(mView);
3837     }
3838 
addOnGlobalLayoutListener(ViewTreeObserver.OnGlobalLayoutListener listener)3839     public void addOnGlobalLayoutListener(ViewTreeObserver.OnGlobalLayoutListener listener) {
3840         mView.getViewTreeObserver().addOnGlobalLayoutListener(listener);
3841     }
3842 
removeOnGlobalLayoutListener(ViewTreeObserver.OnGlobalLayoutListener listener)3843     public void removeOnGlobalLayoutListener(ViewTreeObserver.OnGlobalLayoutListener listener) {
3844         mView.getViewTreeObserver().removeOnGlobalLayoutListener(listener);
3845     }
3846 
getOnHeadsUpChangedListener()3847     public MyOnHeadsUpChangedListener getOnHeadsUpChangedListener() {
3848         return mOnHeadsUpChangedListener;
3849     }
3850 
getHeight()3851     public int getHeight() {
3852         return mView.getHeight();
3853     }
3854 
setHeaderDebugInfo(String text)3855     public void setHeaderDebugInfo(String text) {
3856         if (DEBUG) mHeaderDebugInfo = text;
3857     }
3858 
onThemeChanged()3859     public void onThemeChanged() {
3860         mConfigurationListener.onThemeChanged();
3861     }
3862 
3863     @Override
createLayoutChangeListener()3864     public OnLayoutChangeListener createLayoutChangeListener() {
3865         return new OnLayoutChangeListener();
3866     }
3867 
3868     @Override
createTouchHandler()3869     protected TouchHandler createTouchHandler() {
3870         return new TouchHandler() {
3871 
3872             private long mLastTouchDownTime = -1L;
3873 
3874             @Override
3875             public boolean onInterceptTouchEvent(MotionEvent event) {
3876                 if (mBlockTouches || mQsFullyExpanded && mQs.disallowPanelTouches()) {
3877                     return false;
3878                 }
3879                 initDownStates(event);
3880                 // Do not let touches go to shade or QS if the bouncer is visible,
3881                 // but still let user swipe down to expand the panel, dismissing the bouncer.
3882                 if (mStatusBar.isBouncerShowing()) {
3883                     return true;
3884                 }
3885                 if (mBar.panelEnabled() && mHeadsUpTouchHelper.onInterceptTouchEvent(event)) {
3886                     mMetricsLogger.count(COUNTER_PANEL_OPEN, 1);
3887                     mMetricsLogger.count(COUNTER_PANEL_OPEN_PEEK, 1);
3888                     return true;
3889                 }
3890                 if (!shouldQuickSettingsIntercept(mDownX, mDownY, 0)
3891                         && mPulseExpansionHandler.onInterceptTouchEvent(event)) {
3892                     return true;
3893                 }
3894 
3895                 if (!isFullyCollapsed() && onQsIntercept(event)) {
3896                     return true;
3897                 }
3898                 return super.onInterceptTouchEvent(event);
3899             }
3900 
3901             @Override
3902             public boolean onTouch(View v, MotionEvent event) {
3903                 if (event.getAction() == MotionEvent.ACTION_DOWN) {
3904                     if (event.getDownTime() == mLastTouchDownTime) {
3905                         // An issue can occur when swiping down after unlock, where multiple down
3906                         // events are received in this handler with identical downTimes. Until the
3907                         // source of the issue can be located, detect this case and ignore.
3908                         // see b/193350347
3909                         Log.w(TAG, "Duplicate down event detected... ignoring");
3910                         return true;
3911                     }
3912                     mLastTouchDownTime = event.getDownTime();
3913                 }
3914 
3915 
3916                 if (mBlockTouches || (mQsFullyExpanded && mQs != null
3917                         && mQs.disallowPanelTouches())) {
3918                     return false;
3919                 }
3920 
3921                 // Do not allow panel expansion if bouncer is scrimmed, otherwise user would be able
3922                 // to pull down QS or expand the shade.
3923                 if (mStatusBar.isBouncerShowingScrimmed()) {
3924                     return false;
3925                 }
3926 
3927                 // Make sure the next touch won't the blocked after the current ends.
3928                 if (event.getAction() == MotionEvent.ACTION_UP
3929                         || event.getAction() == MotionEvent.ACTION_CANCEL) {
3930                     mBlockingExpansionForCurrentTouch = false;
3931                 }
3932                 // When touch focus transfer happens, ACTION_DOWN->ACTION_UP may happen immediately
3933                 // without any ACTION_MOVE event.
3934                 // In such case, simply expand the panel instead of being stuck at the bottom bar.
3935                 if (mLastEventSynthesizedDown && event.getAction() == MotionEvent.ACTION_UP) {
3936                     expand(true /* animate */);
3937                 }
3938                 initDownStates(event);
3939 
3940                 // If pulse is expanding already, let's give it the touch. There are situations
3941                 // where the panel starts expanding even though we're also pulsing
3942                 boolean pulseShouldGetTouch = (!mIsExpanding
3943                         && !shouldQuickSettingsIntercept(mDownX, mDownY, 0))
3944                         || mPulseExpansionHandler.isExpanding();
3945                 if (pulseShouldGetTouch && mPulseExpansionHandler.onTouchEvent(event)) {
3946                     // We're expanding all the other ones shouldn't get this anymore
3947                     return true;
3948                 }
3949                 if (mListenForHeadsUp && !mHeadsUpTouchHelper.isTrackingHeadsUp()
3950                         && mHeadsUpTouchHelper.onInterceptTouchEvent(event)) {
3951                     mMetricsLogger.count(COUNTER_PANEL_OPEN_PEEK, 1);
3952                 }
3953                 boolean handled = false;
3954                 if ((!mIsExpanding || mHintAnimationRunning) && !mQsExpanded
3955                         && mBarState != StatusBarState.SHADE && !mDozing) {
3956                     handled |= mAffordanceHelper.onTouchEvent(event);
3957                 }
3958                 if (mOnlyAffordanceInThisMotion) {
3959                     return true;
3960                 }
3961                 handled |= mHeadsUpTouchHelper.onTouchEvent(event);
3962 
3963                 if (!mHeadsUpTouchHelper.isTrackingHeadsUp() && handleQsTouch(event)) {
3964                     return true;
3965                 }
3966                 if (event.getActionMasked() == MotionEvent.ACTION_DOWN && isFullyCollapsed()) {
3967                     mMetricsLogger.count(COUNTER_PANEL_OPEN, 1);
3968                     updateHorizontalPanelPosition(event.getX());
3969                     handled = true;
3970                 }
3971 
3972                 if (event.getActionMasked() == MotionEvent.ACTION_DOWN && isFullyExpanded()
3973                         && mStatusBarKeyguardViewManager.isShowing()) {
3974                     mStatusBarKeyguardViewManager.updateKeyguardPosition(event.getX());
3975                 }
3976 
3977                 if (mLockIconViewController.onTouchEvent(event)) {
3978                     return true;
3979                 }
3980 
3981                 handled |= super.onTouch(v, event);
3982                 return !mDozing || mPulsing || handled;
3983             }
3984         };
3985     }
3986 
3987     @Override
3988     protected PanelViewController.OnConfigurationChangedListener
createOnConfigurationChangedListener()3989             createOnConfigurationChangedListener() {
3990         return new OnConfigurationChangedListener();
3991     }
3992 
getNotificationStackScrollLayoutController()3993     public NotificationStackScrollLayoutController getNotificationStackScrollLayoutController() {
3994         return mNotificationStackScrollLayoutController;
3995     }
3996 
3997     /**
3998      * Close the keyguard user switcher if it is open and capable of closing.
3999      *
4000      * Has no effect if user switcher isn't supported, if the user switcher is already closed, or
4001      * if the user switcher uses "simple" mode. The simple user switcher cannot be closed.
4002      *
4003      * @return true if the keyguard user switcher was open, and is now closed
4004      */
closeUserSwitcherIfOpen()4005     public boolean closeUserSwitcherIfOpen() {
4006         if (mKeyguardUserSwitcherController != null) {
4007             return mKeyguardUserSwitcherController.closeSwitcherIfOpenAndNotSimple(
4008                     true /* animate */);
4009         }
4010         return false;
4011     }
4012 
updateUserSwitcherFlags()4013     private void updateUserSwitcherFlags() {
4014         mKeyguardUserSwitcherEnabled = mResources.getBoolean(
4015                 com.android.internal.R.bool.config_keyguardUserSwitcher);
4016         mKeyguardQsUserSwitchEnabled =
4017                 mKeyguardUserSwitcherEnabled && mResources.getBoolean(
4018                         R.bool.config_keyguard_user_switch_opens_qs_details);
4019     }
4020 
registerSettingsChangeListener()4021     private void registerSettingsChangeListener() {
4022         mContentResolver.registerContentObserver(
4023                 Settings.Global.getUriFor(Settings.Global.USER_SWITCHER_ENABLED),
4024                 /* notifyForDescendants */ false,
4025                 mSettingsChangeObserver
4026         );
4027     }
4028 
unregisterSettingsChangeListener()4029     private void unregisterSettingsChangeListener() {
4030         mContentResolver.unregisterContentObserver(mSettingsChangeObserver);
4031     }
4032 
4033     private class OnHeightChangedListener implements ExpandableView.OnHeightChangedListener {
4034         @Override
onHeightChanged(ExpandableView view, boolean needsAnimation)4035         public void onHeightChanged(ExpandableView view, boolean needsAnimation) {
4036 
4037             // Block update if we are in quick settings and just the top padding changed
4038             // (i.e. view == null).
4039             if (view == null && mQsExpanded) {
4040                 return;
4041             }
4042             if (needsAnimation && mInterpolatedDarkAmount == 0) {
4043                 mAnimateNextPositionUpdate = true;
4044             }
4045             ExpandableView firstChildNotGone =
4046                     mNotificationStackScrollLayoutController.getFirstChildNotGone();
4047             ExpandableNotificationRow
4048                     firstRow =
4049                     firstChildNotGone instanceof ExpandableNotificationRow
4050                             ? (ExpandableNotificationRow) firstChildNotGone : null;
4051             if (firstRow != null && (view == firstRow || (firstRow.getNotificationParent()
4052                     == firstRow))) {
4053                 requestScrollerTopPaddingUpdate(false /* animate */);
4054             }
4055             requestPanelHeightUpdate();
4056         }
4057 
4058         @Override
onReset(ExpandableView view)4059         public void onReset(ExpandableView view) {
4060         }
4061     }
4062 
4063     private class OnClickListener implements View.OnClickListener {
4064         @Override
onClick(View v)4065         public void onClick(View v) {
4066             onQsExpansionStarted();
4067             if (mQsExpanded) {
4068                 flingSettings(0 /* vel */, FLING_COLLAPSE, null /* onFinishRunnable */,
4069                         true /* isClick */);
4070             } else if (isQsExpansionEnabled()) {
4071                 mLockscreenGestureLogger.write(MetricsEvent.ACTION_SHADE_QS_TAP, 0, 0);
4072                 flingSettings(0 /* vel */, FLING_EXPAND, null /* onFinishRunnable */,
4073                         true /* isClick */);
4074             }
4075         }
4076     }
4077 
4078     private class OnOverscrollTopChangedListener implements
4079             NotificationStackScrollLayout.OnOverscrollTopChangedListener {
4080         @Override
onOverscrollTopChanged(float amount, boolean isRubberbanded)4081         public void onOverscrollTopChanged(float amount, boolean isRubberbanded) {
4082             // When in split shade, overscroll shouldn't carry through to QS
4083             if (mShouldUseSplitNotificationShade) {
4084                 return;
4085             }
4086             cancelQsAnimation();
4087             if (!isQsExpansionEnabled()) {
4088                 amount = 0f;
4089             }
4090             float rounded = amount >= 1f ? amount : 0f;
4091             setOverScrolling(rounded != 0f && isRubberbanded);
4092             mQsExpansionFromOverscroll = rounded != 0f;
4093             mLastOverscroll = rounded;
4094             updateQsState();
4095             setQsExpansion(mQsMinExpansionHeight + rounded);
4096         }
4097 
4098         @Override
flingTopOverscroll(float velocity, boolean open)4099         public void flingTopOverscroll(float velocity, boolean open) {
4100             // in split shade mode we want to expand/collapse QS only when touch happens within QS
4101             if (mShouldUseSplitNotificationShade
4102                     && (mInitialTouchX < mQsFrame.getX()
4103                         || mInitialTouchX > mQsFrame.getX() + mQsFrame.getWidth())) {
4104                 return;
4105             }
4106             mLastOverscroll = 0f;
4107             mQsExpansionFromOverscroll = false;
4108             if (open) {
4109                 // During overscrolling, qsExpansion doesn't actually change that the qs is
4110                 // becoming expanded. Any layout could therefore reset the position again. Let's
4111                 // make sure we can expand
4112                 setOverScrolling(false);
4113             }
4114             setQsExpansion(mQsExpansionHeight);
4115             boolean canExpand = isQsExpansionEnabled();
4116             flingSettings(!canExpand && open ? 0f : velocity,
4117                     open && canExpand ? FLING_EXPAND : FLING_COLLAPSE, () -> {
4118                         setOverScrolling(false);
4119                         updateQsState();
4120                     }, false /* isClick */);
4121         }
4122     }
4123 
4124     private class DynamicPrivacyControlListener implements DynamicPrivacyController.Listener {
4125         @Override
onDynamicPrivacyChanged()4126         public void onDynamicPrivacyChanged() {
4127             // Do not request animation when pulsing or waking up, otherwise the clock wiill be out
4128             // of sync with the notification panel.
4129             if (mLinearDarkAmount != 0) {
4130                 return;
4131             }
4132             mAnimateNextPositionUpdate = true;
4133         }
4134     }
4135 
4136     private class KeyguardAffordanceHelperCallback implements KeyguardAffordanceHelper.Callback {
4137         @Override
onAnimationToSideStarted(boolean rightPage, float translation, float vel)4138         public void onAnimationToSideStarted(boolean rightPage, float translation, float vel) {
4139             boolean
4140                     start =
4141                     mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL ? rightPage
4142                             : !rightPage;
4143             mIsLaunchTransitionRunning = true;
4144             mLaunchAnimationEndRunnable = null;
4145             float displayDensity = mStatusBar.getDisplayDensity();
4146             int lengthDp = Math.abs((int) (translation / displayDensity));
4147             int velocityDp = Math.abs((int) (vel / displayDensity));
4148             if (start) {
4149                 mLockscreenGestureLogger.write(MetricsEvent.ACTION_LS_DIALER, lengthDp, velocityDp);
4150                 mLockscreenGestureLogger.log(LockscreenUiEvent.LOCKSCREEN_DIALER);
4151                 mFalsingCollector.onLeftAffordanceOn();
4152                 if (mFalsingCollector.shouldEnforceBouncer()) {
4153                     mStatusBar.executeRunnableDismissingKeyguard(
4154                             () -> mKeyguardBottomArea.launchLeftAffordance(), null,
4155                             true /* dismissShade */, false /* afterKeyguardGone */,
4156                             true /* deferred */);
4157                 } else {
4158                     mKeyguardBottomArea.launchLeftAffordance();
4159                 }
4160             } else {
4161                 if (KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_AFFORDANCE.equals(
4162                         mLastCameraLaunchSource)) {
4163                     mLockscreenGestureLogger.write(
4164                             MetricsEvent.ACTION_LS_CAMERA, lengthDp, velocityDp);
4165                     mLockscreenGestureLogger.log(LockscreenUiEvent.LOCKSCREEN_CAMERA);
4166                 }
4167                 mFalsingCollector.onCameraOn();
4168                 if (mFalsingCollector.shouldEnforceBouncer()) {
4169                     mStatusBar.executeRunnableDismissingKeyguard(
4170                             () -> mKeyguardBottomArea.launchCamera(mLastCameraLaunchSource), null,
4171                             true /* dismissShade */, false /* afterKeyguardGone */,
4172                             true /* deferred */);
4173                 } else {
4174                     mKeyguardBottomArea.launchCamera(mLastCameraLaunchSource);
4175                 }
4176             }
4177             mStatusBar.startLaunchTransitionTimeout();
4178             mBlockTouches = true;
4179         }
4180 
4181         @Override
onAnimationToSideEnded()4182         public void onAnimationToSideEnded() {
4183             mIsLaunchTransitionRunning = false;
4184             mIsLaunchTransitionFinished = true;
4185             if (mLaunchAnimationEndRunnable != null) {
4186                 mLaunchAnimationEndRunnable.run();
4187                 mLaunchAnimationEndRunnable = null;
4188             }
4189             mStatusBar.readyForKeyguardDone();
4190         }
4191 
4192         @Override
getMaxTranslationDistance()4193         public float getMaxTranslationDistance() {
4194             return (float) Math.hypot(mView.getWidth(), getHeight());
4195         }
4196 
4197         @Override
onSwipingStarted(boolean rightIcon)4198         public void onSwipingStarted(boolean rightIcon) {
4199             mFalsingCollector.onAffordanceSwipingStarted(rightIcon);
4200             boolean
4201                     camera =
4202                     mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL ? !rightIcon
4203                             : rightIcon;
4204             if (camera) {
4205                 mKeyguardBottomArea.bindCameraPrewarmService();
4206             }
4207             mView.requestDisallowInterceptTouchEvent(true);
4208             mOnlyAffordanceInThisMotion = true;
4209             mQsTracking = false;
4210         }
4211 
4212         @Override
onSwipingAborted()4213         public void onSwipingAborted() {
4214             mFalsingCollector.onAffordanceSwipingAborted();
4215             mKeyguardBottomArea.unbindCameraPrewarmService(false /* launched */);
4216         }
4217 
4218         @Override
onIconClicked(boolean rightIcon)4219         public void onIconClicked(boolean rightIcon) {
4220             if (mHintAnimationRunning) {
4221                 return;
4222             }
4223             mHintAnimationRunning = true;
4224             mAffordanceHelper.startHintAnimation(rightIcon, () -> {
4225                 mHintAnimationRunning = false;
4226                 mStatusBar.onHintFinished();
4227             });
4228             rightIcon =
4229                     mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL ? !rightIcon
4230                             : rightIcon;
4231             if (rightIcon) {
4232                 mStatusBar.onCameraHintStarted();
4233             } else {
4234                 if (mKeyguardBottomArea.isLeftVoiceAssist()) {
4235                     mStatusBar.onVoiceAssistHintStarted();
4236                 } else {
4237                     mStatusBar.onPhoneHintStarted();
4238                 }
4239             }
4240         }
4241 
4242         @Override
getLeftIcon()4243         public KeyguardAffordanceView getLeftIcon() {
4244             return mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL
4245                     ? mKeyguardBottomArea.getRightView() : mKeyguardBottomArea.getLeftView();
4246         }
4247 
4248         @Override
getRightIcon()4249         public KeyguardAffordanceView getRightIcon() {
4250             return mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL
4251                     ? mKeyguardBottomArea.getLeftView() : mKeyguardBottomArea.getRightView();
4252         }
4253 
4254         @Override
getLeftPreview()4255         public View getLeftPreview() {
4256             return mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL
4257                     ? mKeyguardBottomArea.getRightPreview() : mKeyguardBottomArea.getLeftPreview();
4258         }
4259 
4260         @Override
getRightPreview()4261         public View getRightPreview() {
4262             return mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL
4263                     ? mKeyguardBottomArea.getLeftPreview() : mKeyguardBottomArea.getRightPreview();
4264         }
4265 
4266         @Override
getAffordanceFalsingFactor()4267         public float getAffordanceFalsingFactor() {
4268             return mStatusBar.isWakeUpComingFromTouch() ? 1.5f : 1.0f;
4269         }
4270 
4271         @Override
needsAntiFalsing()4272         public boolean needsAntiFalsing() {
4273             return mBarState == KEYGUARD;
4274         }
4275     }
4276 
4277     private class OnEmptySpaceClickListener implements
4278             NotificationStackScrollLayout.OnEmptySpaceClickListener {
4279         @Override
onEmptySpaceClicked(float x, float y)4280         public void onEmptySpaceClicked(float x, float y) {
4281             onEmptySpaceClick(x);
4282         }
4283     }
4284 
4285     private class MyOnHeadsUpChangedListener implements OnHeadsUpChangedListener {
4286         @Override
onHeadsUpPinnedModeChanged(final boolean inPinnedMode)4287         public void onHeadsUpPinnedModeChanged(final boolean inPinnedMode) {
4288             if (inPinnedMode) {
4289                 mHeadsUpExistenceChangedRunnable.run();
4290                 updateNotificationTranslucency();
4291             } else {
4292                 setHeadsUpAnimatingAway(true);
4293                 mNotificationStackScrollLayoutController.runAfterAnimationFinished(
4294                         mHeadsUpExistenceChangedRunnable);
4295             }
4296             updateGestureExclusionRect();
4297             mHeadsUpPinnedMode = inPinnedMode;
4298             updateHeadsUpVisibility();
4299             updateKeyguardStatusBarForHeadsUp();
4300         }
4301 
4302         @Override
onHeadsUpPinned(NotificationEntry entry)4303         public void onHeadsUpPinned(NotificationEntry entry) {
4304             if (!isOnKeyguard()) {
4305                 mNotificationStackScrollLayoutController.generateHeadsUpAnimation(
4306                         entry.getHeadsUpAnimationView(), true);
4307             }
4308         }
4309 
4310         @Override
onHeadsUpUnPinned(NotificationEntry entry)4311         public void onHeadsUpUnPinned(NotificationEntry entry) {
4312 
4313             // When we're unpinning the notification via active edge they remain heads-upped,
4314             // we need to make sure that an animation happens in this case, otherwise the
4315             // notification
4316             // will stick to the top without any interaction.
4317             if (isFullyCollapsed() && entry.isRowHeadsUp() && !isOnKeyguard()) {
4318                 mNotificationStackScrollLayoutController.generateHeadsUpAnimation(
4319                         entry.getHeadsUpAnimationView(), false);
4320                 entry.setHeadsUpIsVisible();
4321             }
4322         }
4323     }
4324 
4325     private class HeightListener implements QS.HeightListener {
onQsHeightChanged()4326         public void onQsHeightChanged() {
4327             mQsMaxExpansionHeight = mQs != null ? mQs.getDesiredHeight() : 0;
4328             if (mQsExpanded && mQsFullyExpanded) {
4329                 mQsExpansionHeight = mQsMaxExpansionHeight;
4330                 requestScrollerTopPaddingUpdate(false /* animate */);
4331                 requestPanelHeightUpdate();
4332             }
4333             if (mAccessibilityManager.isEnabled()) {
4334                 mView.setAccessibilityPaneTitle(determineAccessibilityPaneTitle());
4335             }
4336             mNotificationStackScrollLayoutController.setMaxTopPadding(mQsMaxExpansionHeight);
4337         }
4338     }
4339 
4340     private class ConfigurationListener implements ConfigurationController.ConfigurationListener {
4341         @Override
onThemeChanged()4342         public void onThemeChanged() {
4343             if (DEBUG) Log.d(TAG, "onThemeChanged");
4344             final int themeResId = mView.getContext().getThemeResId();
4345             if (mThemeResId == themeResId) {
4346                 return;
4347             }
4348             mThemeResId = themeResId;
4349 
4350             reInflateViews();
4351         }
4352 
4353         @Override
onSmallestScreenWidthChanged()4354         public void onSmallestScreenWidthChanged() {
4355             if (DEBUG) Log.d(TAG, "onSmallestScreenWidthChanged");
4356 
4357             // Can affect multi-user switcher visibility as it depends on screen size by default:
4358             // it is enabled only for devices with large screens (see config_keyguardUserSwitcher)
4359             reInflateViews();
4360         }
4361 
4362         @Override
onOverlayChanged()4363         public void onOverlayChanged() {
4364             if (DEBUG) Log.d(TAG, "onOverlayChanged");
4365             reInflateViews();
4366         }
4367 
4368         @Override
onDensityOrFontScaleChanged()4369         public void onDensityOrFontScaleChanged() {
4370             if (DEBUG) Log.d(TAG, "onDensityOrFontScaleChanged");
4371             reInflateViews();
4372         }
4373     }
4374 
4375     private class SettingsChangeObserver extends ContentObserver {
4376 
SettingsChangeObserver(Handler handler)4377         SettingsChangeObserver(Handler handler) {
4378             super(handler);
4379         }
4380 
4381         @Override
onChange(boolean selfChange)4382         public void onChange(boolean selfChange) {
4383             if (DEBUG) Log.d(TAG, "onSettingsChanged");
4384 
4385             // Can affect multi-user switcher visibility
4386             reInflateViews();
4387         }
4388     }
4389 
4390     private class StatusBarStateListener implements StateListener {
4391         @Override
onStateChanged(int statusBarState)4392         public void onStateChanged(int statusBarState) {
4393             boolean goingToFullShade = mStatusBarStateController.goingToFullShade();
4394             boolean keyguardFadingAway = mKeyguardStateController.isKeyguardFadingAway();
4395             int oldState = mBarState;
4396             boolean keyguardShowing = statusBarState == KEYGUARD;
4397 
4398             if (mDozeParameters.shouldControlUnlockedScreenOff()
4399                     && oldState == StatusBarState.SHADE
4400                     && statusBarState == KEYGUARD) {
4401                 // This means we're doing the screen off animation - position the keyguard status
4402                 // view where it'll be on AOD, so we can animate it in.
4403                 mKeyguardStatusViewController.updatePosition(
4404                         mClockPositionResult.clockX,
4405                         mClockPositionResult.clockYFullyDozing,
4406                         mClockPositionResult.clockScale,
4407                         false /* animate */);
4408             }
4409 
4410             mKeyguardStatusViewController.setKeyguardStatusViewVisibility(
4411                     statusBarState,
4412                     keyguardFadingAway,
4413                     goingToFullShade,
4414                     mBarState);
4415             setKeyguardBottomAreaVisibility(statusBarState, goingToFullShade);
4416 
4417             mBarState = statusBarState;
4418             mKeyguardShowing = keyguardShowing;
4419 
4420             if (oldState == KEYGUARD && (goingToFullShade
4421                     || statusBarState == StatusBarState.SHADE_LOCKED)) {
4422                 animateKeyguardStatusBarOut();
4423                 updateQSMinHeight();
4424             } else if (oldState == StatusBarState.SHADE_LOCKED
4425                     && statusBarState == KEYGUARD) {
4426                 animateKeyguardStatusBarIn(StackStateAnimator.ANIMATION_DURATION_STANDARD);
4427                 mNotificationStackScrollLayoutController.resetScrollPosition();
4428                 // Only animate header if the header is visible. If not, it will partially
4429                 // animate out
4430                 // the top of QS
4431                 if (!mQsExpanded) {
4432                     // TODO(b/185683835) Nicer clipping when using new spacial model
4433                     if (mShouldUseSplitNotificationShade) {
4434                         mQs.animateHeaderSlidingOut();
4435                     }
4436                 }
4437             } else {
4438                 mKeyguardStatusBar.setAlpha(1f);
4439                 mKeyguardStatusBar.setVisibility(keyguardShowing ? View.VISIBLE : View.INVISIBLE);
4440                 if (keyguardShowing && oldState != mBarState) {
4441                     if (mQs != null) {
4442                         mQs.hideImmediately();
4443                     }
4444                 }
4445             }
4446             updateKeyguardStatusBarForHeadsUp();
4447             if (keyguardShowing) {
4448                 updateDozingVisibilities(false /* animate */);
4449             }
4450 
4451             updateMaxDisplayedNotifications(false);
4452             // The update needs to happen after the headerSlide in above, otherwise the translation
4453             // would reset
4454             maybeAnimateBottomAreaAlpha();
4455             resetHorizontalPanelPosition();
4456             updateQsState();
4457         }
4458 
4459         @Override
onDozeAmountChanged(float linearAmount, float amount)4460         public void onDozeAmountChanged(float linearAmount, float amount) {
4461             mInterpolatedDarkAmount = amount;
4462             mLinearDarkAmount = linearAmount;
4463             mKeyguardStatusViewController.setDarkAmount(mInterpolatedDarkAmount);
4464             mKeyguardBottomArea.setDarkAmount(mInterpolatedDarkAmount);
4465             positionClockAndNotifications();
4466         }
4467     }
4468 
4469     /**
4470      * Reconfigures the shade to show the AOD UI (clock, smartspace, etc). This is called by the
4471      * screen off animation controller in order to animate in AOD without "actually" fully switching
4472      * to the KEYGUARD state, which is a heavy transition that causes jank as 10+ files react to the
4473      * change.
4474      */
showAodUi()4475     public void showAodUi() {
4476         setDozing(true /* dozing */, false /* animate */, null);
4477         mStatusBarStateController.setUpcomingState(KEYGUARD);
4478         mEntryManager.updateNotifications("showAodUi");
4479         mStatusBarStateListener.onStateChanged(KEYGUARD);
4480         mStatusBarStateListener.onDozeAmountChanged(1f, 1f);
4481         setExpandedFraction(1f);
4482     }
4483 
4484     /**
4485      * Sets the overstretch amount in raw pixels when dragging down.
4486      */
setOverStrechAmount(float amount)4487     public void setOverStrechAmount(float amount) {
4488         float progress = amount / mView.getHeight();
4489         float overstretch = Interpolators.getOvershootInterpolation(progress);
4490         mOverStretchAmount = overstretch * mMaxOverscrollAmountForPulse;
4491         positionClockAndNotifications(true /* forceUpdate */);
4492     }
4493 
4494     private class OnAttachStateChangeListener implements View.OnAttachStateChangeListener {
4495         @Override
onViewAttachedToWindow(View v)4496         public void onViewAttachedToWindow(View v) {
4497             mFragmentService.getFragmentHostManager(mView)
4498                             .addTagListener(QS.TAG, mFragmentListener);
4499             mStatusBarStateController.addCallback(mStatusBarStateListener);
4500             mConfigurationController.addCallback(mConfigurationListener);
4501             mUpdateMonitor.registerCallback(mKeyguardUpdateCallback);
4502             // Theme might have changed between inflating this view and attaching it to the
4503             // window, so
4504             // force a call to onThemeChanged
4505             mConfigurationListener.onThemeChanged();
4506             mFalsingManager.addTapListener(mFalsingTapListener);
4507             mKeyguardIndicationController.init();
4508             registerSettingsChangeListener();
4509         }
4510 
4511         @Override
onViewDetachedFromWindow(View v)4512         public void onViewDetachedFromWindow(View v) {
4513             unregisterSettingsChangeListener();
4514             mFragmentService.getFragmentHostManager(mView)
4515                             .removeTagListener(QS.TAG, mFragmentListener);
4516             mStatusBarStateController.removeCallback(mStatusBarStateListener);
4517             mConfigurationController.removeCallback(mConfigurationListener);
4518             mUpdateMonitor.removeCallback(mKeyguardUpdateCallback);
4519             mFalsingManager.removeTapListener(mFalsingTapListener);
4520         }
4521     }
4522 
4523     private class OnLayoutChangeListener extends PanelViewController.OnLayoutChangeListener {
4524 
4525         @Override
onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom)4526         public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft,
4527                 int oldTop, int oldRight, int oldBottom) {
4528             DejankUtils.startDetectingBlockingIpcs("NVP#onLayout");
4529             super.onLayoutChange(v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom);
4530             updateMaxDisplayedNotifications(true);
4531             setIsFullWidth(mNotificationStackScrollLayoutController.getWidth() == mView.getWidth());
4532 
4533             // Update Clock Pivot
4534             mKeyguardStatusViewController.setPivotX(mView.getWidth() / 2);
4535             mKeyguardStatusViewController.setPivotY(
4536                     (FONT_HEIGHT - CAP_HEIGHT) / 2048f
4537                             * mKeyguardStatusViewController.getClockTextSize());
4538 
4539             // Calculate quick setting heights.
4540             int oldMaxHeight = mQsMaxExpansionHeight;
4541             if (mQs != null) {
4542                 updateQSMinHeight();
4543                 mQsMaxExpansionHeight = mQs.getDesiredHeight();
4544                 mNotificationStackScrollLayoutController.setMaxTopPadding(mQsMaxExpansionHeight);
4545             }
4546             positionClockAndNotifications();
4547             if (mQsExpanded && mQsFullyExpanded) {
4548                 mQsExpansionHeight = mQsMaxExpansionHeight;
4549                 requestScrollerTopPaddingUpdate(false /* animate */);
4550                 requestPanelHeightUpdate();
4551 
4552                 // Size has changed, start an animation.
4553                 if (mQsMaxExpansionHeight != oldMaxHeight) {
4554                     startQsSizeChangeAnimation(oldMaxHeight, mQsMaxExpansionHeight);
4555                 }
4556             } else if (!mQsExpanded && mQsExpansionAnimator == null) {
4557                 setQsExpansion(mQsMinExpansionHeight + mLastOverscroll);
4558             }
4559             updateExpandedHeight(getExpandedHeight());
4560             updateHeader();
4561 
4562             // If we are running a size change animation, the animation takes care of the height of
4563             // the container. However, if we are not animating, we always need to make the QS
4564             // container
4565             // the desired height so when closing the QS detail, it stays smaller after the size
4566             // change
4567             // animation is finished but the detail view is still being animated away (this
4568             // animation
4569             // takes longer than the size change animation).
4570             if (mQsSizeChangeAnimator == null && mQs != null) {
4571                 mQs.setHeightOverride(mQs.getDesiredHeight());
4572             }
4573             updateMaxHeadsUpTranslation();
4574             updateGestureExclusionRect();
4575             if (mExpandAfterLayoutRunnable != null) {
4576                 mExpandAfterLayoutRunnable.run();
4577                 mExpandAfterLayoutRunnable = null;
4578             }
4579             DejankUtils.stopDetectingBlockingIpcs("NVP#onLayout");
4580         }
4581     }
4582 
updateQSMinHeight()4583     private void updateQSMinHeight() {
4584         float previousMin = mQsMinExpansionHeight;
4585         mQsMinExpansionHeight = mKeyguardShowing ? 0 : mQs.getQsMinExpansionHeight();
4586         if (mQsExpansionHeight == previousMin) {
4587             mQsExpansionHeight = mQsMinExpansionHeight;
4588         }
4589     }
4590 
4591     private class DebugDrawable extends Drawable {
4592 
4593         @Override
draw(Canvas canvas)4594         public void draw(Canvas canvas) {
4595             Paint p = new Paint();
4596             p.setColor(Color.RED);
4597             p.setStrokeWidth(2);
4598             p.setStyle(Paint.Style.STROKE);
4599             canvas.drawLine(0, getMaxPanelHeight(), mView.getWidth(), getMaxPanelHeight(), p);
4600             p.setTextSize(24);
4601             if (mHeaderDebugInfo != null) canvas.drawText(mHeaderDebugInfo, 50, 100, p);
4602             p.setColor(Color.BLUE);
4603             canvas.drawLine(0, getExpandedHeight(), mView.getWidth(), getExpandedHeight(), p);
4604             p.setColor(Color.GREEN);
4605             canvas.drawLine(0, calculatePanelHeightQsExpanded(), mView.getWidth(),
4606                     calculatePanelHeightQsExpanded(), p);
4607             p.setColor(Color.YELLOW);
4608             canvas.drawLine(0, calculatePanelHeightShade(), mView.getWidth(),
4609                     calculatePanelHeightShade(), p);
4610             p.setColor(Color.MAGENTA);
4611             canvas.drawLine(
4612                     0, calculateNotificationsTopPadding(), mView.getWidth(),
4613                     calculateNotificationsTopPadding(), p);
4614             p.setColor(Color.CYAN);
4615             canvas.drawLine(0, mClockPositionResult.stackScrollerPadding, mView.getWidth(),
4616                     mNotificationStackScrollLayoutController.getTopPadding(), p);
4617             p.setColor(Color.GRAY);
4618             canvas.drawLine(0, mClockPositionResult.clockY, mView.getWidth(),
4619                     mClockPositionResult.clockY, p);
4620         }
4621 
4622         @Override
setAlpha(int alpha)4623         public void setAlpha(int alpha) {
4624 
4625         }
4626 
4627         @Override
setColorFilter(ColorFilter colorFilter)4628         public void setColorFilter(ColorFilter colorFilter) {
4629 
4630         }
4631 
4632         @Override
getOpacity()4633         public int getOpacity() {
4634             return 0;
4635         }
4636     }
4637 
4638     private class OnConfigurationChangedListener extends
4639             PanelViewController.OnConfigurationChangedListener {
4640         @Override
onConfigurationChanged(Configuration newConfig)4641         public void onConfigurationChanged(Configuration newConfig) {
4642             super.onConfigurationChanged(newConfig);
4643             mAffordanceHelper.onConfigurationChanged();
4644             if (newConfig.orientation != mLastOrientation) {
4645                 resetHorizontalPanelPosition();
4646             }
4647             mLastOrientation = newConfig.orientation;
4648         }
4649     }
4650 
4651     private class OnApplyWindowInsetsListener implements View.OnApplyWindowInsetsListener {
onApplyWindowInsets(View v, WindowInsets insets)4652         public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) {
4653             // the same types of insets that are handled in NotificationShadeWindowView
4654             int insetTypes = WindowInsets.Type.systemBars() | WindowInsets.Type.displayCutout();
4655             Insets combinedInsets = insets.getInsetsIgnoringVisibility(insetTypes);
4656             mDisplayTopInset = combinedInsets.top;
4657             mDisplayRightInset = combinedInsets.right;
4658 
4659             mNavigationBarBottomHeight = insets.getStableInsetBottom();
4660             updateMaxHeadsUpTranslation();
4661             return insets;
4662         }
4663     }
4664 }
4665