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