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