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