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